Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1479 lines
37 KiB

  1. /*++
  2. Copyright (c) 1987-1991 Microsoft Corporation
  3. Module Name:
  4. alformat.c
  5. Abstract:
  6. This module contains routines to format alert messages sent out by the
  7. Alerter service.
  8. Author:
  9. Ported from LAN Man 2.0
  10. Environment:
  11. User mode only.
  12. Contains NT-specific code.
  13. Requires ANSI C extensions: slash-slash comments, long external names.
  14. Revision History:
  15. 08-July-1991 (ritaw)
  16. Ported to NT. Converted to NT style.
  17. --*/
  18. #include "alformat.h" // Constant definitions
  19. #include <windows.h> // GetDateFormat/GetTimeFormat
  20. //-------------------------------------------------------------------//
  21. // //
  22. // Global variables //
  23. // //
  24. //-------------------------------------------------------------------//
  25. CHAR MessageBuffer[MAX_ALERTER_MESSAGE_SIZE];
  26. //-------------------------------------------------------------------//
  27. // //
  28. // Local function prototypes //
  29. // //
  30. //-------------------------------------------------------------------//
  31. STATIC
  32. NET_API_STATUS
  33. AlMakeMessageHeader(
  34. IN LPTSTR From,
  35. IN LPTSTR To,
  36. IN DWORD MessageSubjectId,
  37. IN DWORD AlertNotificationTime,
  38. IN BOOL IsAdminAlert
  39. );
  40. STATIC
  41. NET_API_STATUS
  42. AlAppendMessage(
  43. IN DWORD MessageId,
  44. OUT LPSTR MessageBuffer,
  45. IN LPSTR *SubstituteStrings,
  46. IN DWORD NumberOfSubstituteStrings
  47. );
  48. STATIC
  49. NET_API_STATUS
  50. AlMakeMessageBody(
  51. IN DWORD MessageId,
  52. IN LPTSTR MergeStrings,
  53. IN DWORD NumberOfMergeStrings
  54. );
  55. STATIC
  56. VOID
  57. AlMakePrintMessage(
  58. IN DWORD PrintMessageID,
  59. IN LPTSTR DocumentName,
  60. IN LPTSTR PrinterName,
  61. IN LPTSTR ServerName,
  62. IN LPTSTR StatusString
  63. );
  64. STATIC
  65. NET_API_STATUS
  66. AlSendMessage(
  67. IN LPTSTR MessageAlias
  68. );
  69. VOID
  70. AlMakeTimeString(
  71. DWORD * Time,
  72. PCHAR String,
  73. int StringLength
  74. );
  75. STATIC
  76. BOOL
  77. IsDuplicateReceiver(
  78. LPTSTR Name
  79. );
  80. STATIC
  81. NET_API_STATUS
  82. AlMakeMessageHeader(
  83. IN LPTSTR From,
  84. IN LPTSTR To,
  85. IN DWORD MessageSubjectId,
  86. IN DWORD AlertNotificationTime,
  87. IN BOOL IsAdminAlert
  88. )
  89. /*++
  90. Routine Description:
  91. This function creates the header of an alert message, and puts it in the
  92. MessageBuffer. A message header takes the following form:
  93. From: SPOOLER at \\PRT41130
  94. To: DAISY
  95. Subj: ** PRINTING NOTIFICATION **
  96. Date: 06-23-91 01:16am
  97. Arguments:
  98. From - Supplies the component that raised the alert.
  99. To - Supplies the message alias name of the recipient.
  100. MessageSubjectId - Supplies an id which indicates the subject of the
  101. alert.
  102. AlertNotificationTime - Supplies the time of the alert notification.
  103. IsAdminAlert - Supplies a flag which if TRUE indicates that the To line
  104. should be created for multiple recipients.
  105. Return Value:
  106. ERROR_NOT_ENOUGH_MEMORY - if Unicode to ANSI conversion buffer cannot
  107. be allocated.
  108. NERR_Success - if successful.
  109. --*/
  110. {
  111. //
  112. // Array of subtitution strings for an alert message
  113. //
  114. LPSTR SubstituteStrings[2];
  115. //
  116. // Tab read in from the message file
  117. //
  118. static CHAR TabMessage[80] = "";
  119. LPSTR AnsiTo;
  120. DWORD ToLineLength; // Number of chars on the To line
  121. WORD MessageLength; // Returned by DosGetMessage
  122. LPSTR AnsiAlertNames;
  123. LPSTR FormatPointer1;
  124. LPSTR FormatPointer2;
  125. CHAR SaveChar;
  126. CHAR szAlertTime[MAX_DATE_TIME_LEN];
  127. LPSTR PlaceHolder = "X";
  128. LPSTR PointerToPlaceHolder = NULL;
  129. //
  130. // Read in the message tab, if necessary
  131. //
  132. if (*TabMessage == AL_NULL_CHAR) {
  133. if (DosGetMessage(
  134. NULL, // String substitution table
  135. 0, // Number of entries in table above
  136. (LPBYTE) TabMessage, // Buffer receiving message
  137. sizeof(TabMessage), // Size of buffer receiving message
  138. APE2_ALERTER_TAB, // Message number to retrieve
  139. MESSAGE_FILENAME, // Name of message file
  140. &MessageLength // Number of bytes returned
  141. )) {
  142. *TabMessage = AL_NULL_CHAR;
  143. }
  144. }
  145. //
  146. // Creating a new alert message
  147. //
  148. MessageBuffer[0] = AL_NULL_CHAR;
  149. //
  150. // "From: <From> at \\<AlLocalComputerName>"
  151. //
  152. // Alerts received by the Alerter service all come from the local server.
  153. //
  154. SubstituteStrings[0] = NetpAllocStrFromWStr(From);
  155. if (SubstituteStrings[0] == NULL) {
  156. return ERROR_NOT_ENOUGH_MEMORY;
  157. }
  158. if (AlLocalComputerNameA != NULL) {
  159. SubstituteStrings[1] = AlLocalComputerNameA;
  160. }
  161. else {
  162. NetApiBufferFree(SubstituteStrings[0]);
  163. return ERROR_GEN_FAILURE;
  164. }
  165. //
  166. // Put the from line into the message buffer
  167. //
  168. AlAppendMessage(
  169. APE2_ALERTER_FROM,
  170. MessageBuffer,
  171. SubstituteStrings,
  172. 2 // Number of strings to substitute into message
  173. );
  174. NetApiBufferFree(SubstituteStrings[0]);
  175. //
  176. // "To: X"
  177. //
  178. // The 'X' is a place holder for the To message so that DosGetMessage
  179. // can perform the substitution. We are not putting the real string
  180. // because the message can either be sent to one recipient (non-admin
  181. // alerts or to many recipient (admin alert).
  182. //
  183. SubstituteStrings[0] = PlaceHolder;
  184. AlAppendMessage(
  185. APE2_ALERTER_TO,
  186. MessageBuffer,
  187. SubstituteStrings,
  188. 1 // Number of strings to substitute into message
  189. );
  190. //
  191. // Search for the place holder character and replace with zero terminator
  192. //
  193. PointerToPlaceHolder = strrchr(MessageBuffer, *PlaceHolder);
  194. //
  195. // If PointerToPlaceHolder == NULL, we have a big problem, but rather than
  196. // choke, we'll just continue. The resulting message will be
  197. // mis-formated (the place holder will still be there and the To name(s)
  198. // will be on the next line) but it will still be sent.
  199. //
  200. if (PointerToPlaceHolder != NULL) {
  201. *PointerToPlaceHolder = AL_NULL_CHAR;
  202. }
  203. if (To != NULL) {
  204. AnsiTo = NetpAllocStrFromWStr(To);
  205. if (AnsiTo == NULL) {
  206. return ERROR_NOT_ENOUGH_MEMORY;
  207. }
  208. }
  209. else {
  210. AnsiTo = NULL;
  211. }
  212. if (IsAdminAlert) {
  213. //
  214. // Do not send the message to the same person twice, since it is
  215. // possible that the person is on the alert names list, as well as
  216. // specified by To.
  217. //
  218. if (To != NULL && ! IsDuplicateReceiver(To)) {
  219. //
  220. // Print admin alerts, like printer is offline or out of paper,
  221. // will be sent to the person who's printing as well as admins
  222. // on the alertnames list.
  223. //
  224. strcat(MessageBuffer, AnsiTo);
  225. strcat(MessageBuffer, " ");
  226. ToLineLength = strlen(TabMessage) + strlen(AnsiTo) + sizeof(CHAR);
  227. }
  228. else {
  229. //
  230. // All admin alerts will have a NULL To field, except print admin
  231. // alerts, e.g. ran out of paper while printing.
  232. //
  233. ToLineLength = strlen(TabMessage);
  234. }
  235. if (AlertNamesA != NULL) {
  236. //
  237. // AlertNamesA is space-separated.
  238. //
  239. AnsiAlertNames = AlertNamesA;
  240. //
  241. // Wrap the names to the next line if width of all alert names exceeds
  242. // screen display width.
  243. //
  244. while ((strlen(AnsiAlertNames) + ToLineLength) > MESSAGE_WIDTH) {
  245. FormatPointer1 = AnsiAlertNames + 1 +
  246. (MESSAGE_WIDTH - ToLineLength);
  247. SaveChar = *FormatPointer1;
  248. *FormatPointer1 = AL_NULL_CHAR;
  249. FormatPointer2 = strrchr(AnsiAlertNames, AL_SPACE_CHAR);
  250. *FormatPointer2 = AL_NULL_CHAR;
  251. strcat(MessageBuffer, AnsiAlertNames);
  252. *FormatPointer2++ = AL_SPACE_CHAR;
  253. *FormatPointer1 = SaveChar;
  254. strcat(MessageBuffer, AL_EOL_STRING);
  255. strcat(MessageBuffer, TabMessage);
  256. AnsiAlertNames = FormatPointer2;
  257. ToLineLength = strlen(TabMessage);
  258. }
  259. strcat(MessageBuffer, AnsiAlertNames);
  260. }
  261. }
  262. else {
  263. //
  264. // Non-admin alert
  265. //
  266. if (To != NULL) {
  267. strcat(MessageBuffer, AnsiTo);
  268. }
  269. }
  270. if (AnsiTo != NULL) {
  271. NetApiBufferFree(AnsiTo);
  272. }
  273. strcat(MessageBuffer, AL_EOL_STRING);
  274. //
  275. // Append subject line to MessageBuffer
  276. //
  277. // "Subj: <Message string of MessageSubjectId>"
  278. //
  279. AlAppendMessage(
  280. MessageSubjectId,
  281. MessageBuffer,
  282. SubstituteStrings,
  283. 0 // No substitution strings
  284. );
  285. AlMakeTimeString(
  286. &AlertNotificationTime,
  287. szAlertTime,
  288. sizeof(szAlertTime)
  289. );
  290. SubstituteStrings[0] = szAlertTime;
  291. //
  292. // "Date: <mm/dd/yy hh:mm>"
  293. //
  294. AlAppendMessage(
  295. APE2_ALERTER_DATE,
  296. MessageBuffer,
  297. SubstituteStrings,
  298. 1
  299. );
  300. strcat(MessageBuffer, AL_EOL_STRING);
  301. return NERR_Success;
  302. }
  303. VOID
  304. AlAdminFormatAndSend(
  305. IN PSTD_ALERT Alert
  306. )
  307. /*++
  308. Routine Description:
  309. This function takes an admin alert notification, formats it into an alert
  310. message, and sends it to the admins with message aliases that are listed
  311. on the alert names entry in the configuration.
  312. An admin alert notification (arrived via the mailslot) has the following
  313. form:
  314. Timestamp of the alert event
  315. "ADMIN"
  316. "SERVER"
  317. Message id of message
  318. Number of merge strings which will be substituted into message
  319. Merge strings, each separated by a zero terminator.
  320. Arguments:
  321. Alert - Supplies a pointer to the alert notification structure.
  322. Return Value:
  323. None.
  324. --*/
  325. {
  326. NET_API_STATUS status;
  327. LPTSTR AdminToAlert;
  328. PADMIN_OTHER_INFO AdminInfo = (PADMIN_OTHER_INFO) ALERT_OTHER_INFO(Alert);
  329. AdminToAlert = AlertNamesW;
  330. IF_DEBUG(FORMAT) {
  331. NetpKdPrint(("[Alerter] Got admin alert\n"));
  332. }
  333. while (AdminToAlert != NULL && *AdminToAlert != TCHAR_EOS) {
  334. //
  335. // Format the message for this alert name
  336. //
  337. status = AlMakeMessageHeader(
  338. Alert->alrt_servicename,
  339. NULL, // The To field is always NULL
  340. APE2_ALERTER_ADMN_SUBJ,
  341. Alert->alrt_timestamp,
  342. TRUE // Admin alert
  343. );
  344. if (status != NERR_Success) {
  345. NetpKdPrint((
  346. "[Alerter] Alert not sent. Error making message header %lu\n",
  347. status
  348. ));
  349. return;
  350. }
  351. AlMakeMessageBody(
  352. AdminInfo->alrtad_errcode,
  353. (LPTSTR) ALERT_VAR_DATA(AdminInfo),
  354. AdminInfo->alrtad_numstrings
  355. );
  356. //
  357. // Send the message
  358. //
  359. (void) AlSendMessage(AdminToAlert);
  360. AdminToAlert += (STRLEN(AdminToAlert) + 1);
  361. }
  362. }
  363. STATIC
  364. NET_API_STATUS
  365. AlMakeMessageBody(
  366. IN DWORD MessageId,
  367. IN LPTSTR MergeStrings,
  368. IN DWORD NumberOfMergeStrings
  369. )
  370. /*++
  371. Routine Description:
  372. This function creates the body of an alert message and append it to the
  373. header already in MessageBuffer.
  374. Arguments:
  375. MessageId - Supplies a message id of the core message to be sent.
  376. MergeStrings - Supplies a pointer to strings that would make up the message
  377. to be sent. The strings are separated by zero terminators.
  378. NumberOfMergeStrings - Supplies the number of strings pointed to by
  379. MergeStrings.
  380. Return Value:
  381. NET_API_STATUS - NERR_Success or reason for failure.
  382. --*/
  383. {
  384. NET_API_STATUS status = NERR_Success;
  385. DWORD i;
  386. LPSTR AdminMessage;
  387. LPSTR MergeTable[9];
  388. CHAR String[34];
  389. LPSTR SubstituteStrings[2];
  390. LPSTR CRPointer;
  391. LPSTR EndPointer;
  392. //
  393. // Message utility can only handle substitution of up to 9 strings.
  394. //
  395. if (NumberOfMergeStrings > 9) {
  396. return ERROR_INVALID_PARAMETER;
  397. }
  398. //
  399. // Allocate memory for the buffer which receives the message from the
  400. // message file.
  401. //
  402. if ((AdminMessage = (LPSTR) LocalAlloc(
  403. LMEM_ZEROINIT,
  404. MAX_ALERTER_MESSAGE_SIZE
  405. )) == NULL) {
  406. return GetLastError();
  407. }
  408. if (MessageId == NO_MESSAGE) {
  409. //
  410. // Merge strings are the literal message (don't format). Print one
  411. // per line.
  412. //
  413. for (i = 0; i < NumberOfMergeStrings; i++) {
  414. SubstituteStrings[0] = NetpAllocStrFromWStr(MergeStrings);
  415. if (SubstituteStrings[0] == NULL) {
  416. (void) LocalFree(AdminMessage);
  417. return ERROR_NOT_ENOUGH_MEMORY;
  418. }
  419. strcat(MessageBuffer, SubstituteStrings[0]);
  420. NetApiBufferFree(SubstituteStrings[0]);
  421. strcat(MessageBuffer, AL_EOL_STRING);
  422. MergeStrings = STRCHR(MergeStrings, TCHAR_EOS);
  423. MergeStrings++;
  424. }
  425. }
  426. else {
  427. //
  428. // Set up the MergeStrings table for the call to AlAppendMessage
  429. //
  430. for (i = 0; i < NumberOfMergeStrings; i++) {
  431. IF_DEBUG(FORMAT) {
  432. NetpKdPrint(("Merge string #%lu: " FORMAT_LPTSTR "\n", i, MergeStrings));
  433. }
  434. MergeTable[i] = NetpAllocStrFromWStr(MergeStrings);
  435. if (MergeTable[i] == NULL) {
  436. DWORD j;
  437. (void) LocalFree(AdminMessage);
  438. for (j = 0; j < i; j++) {
  439. (void) LocalFree(MergeTable[j]);
  440. }
  441. return ERROR_NOT_ENOUGH_MEMORY;
  442. }
  443. MergeStrings = STRCHR(MergeStrings, TCHAR_EOS);
  444. MergeStrings++;
  445. }
  446. status = AlAppendMessage(
  447. MessageId,
  448. AdminMessage,
  449. MergeTable,
  450. NumberOfMergeStrings
  451. );
  452. //
  453. // Free memory allocated for Unicode to ANSI conversion
  454. //
  455. for (i = 0; i < NumberOfMergeStrings; i++) {
  456. (void) LocalFree(MergeTable[i]);
  457. }
  458. if (status != NERR_Success) {
  459. //
  460. // Could not find message of MessageId in message file. An error
  461. // message will be sent.
  462. //
  463. _itoa(MessageId, String, 10);
  464. SubstituteStrings[0] = String;
  465. AlAppendMessage(
  466. APE2_ALERTER_ERROR_MSG,
  467. MessageBuffer,
  468. SubstituteStrings,
  469. 1
  470. );
  471. status = NERR_Success;
  472. }
  473. else {
  474. //
  475. // Got the message
  476. //
  477. //
  478. // Process the message from DosGetMessage to replace the CR and
  479. // LF with equivalent display characters.
  480. //
  481. CRPointer = strchr(AdminMessage, AL_CR_CHAR);
  482. EndPointer = CRPointer;
  483. while (CRPointer) {
  484. *CRPointer = '\040';
  485. CRPointer++;
  486. //
  487. // Check for end of message
  488. //
  489. if (*CRPointer != AL_NULL_CHAR) {
  490. //
  491. // Use display end-of-line character
  492. //
  493. *CRPointer = AL_EOL_CHAR;
  494. }
  495. EndPointer = CRPointer;
  496. CRPointer = strchr(CRPointer, AL_CR_CHAR);
  497. }
  498. //
  499. // Wipe out rest of garbage in the message
  500. //
  501. if (EndPointer) {
  502. *EndPointer = AL_EOL_CHAR;
  503. *(EndPointer + 1) = AL_NULL_CHAR;
  504. }
  505. strcat(MessageBuffer, AdminMessage);
  506. }
  507. }
  508. (void) LocalFree(AdminMessage);
  509. return status;
  510. }
  511. VOID
  512. AlUserFormatAndSend(
  513. IN PSTD_ALERT Alert
  514. )
  515. /*++
  516. Routine Description:
  517. This function takes a user alert notification, formats it into an alert
  518. message, and sends it to the user. If there is an error sending to the
  519. user message alias, the message is send to the computer name.
  520. A user alert notification (arrived via the mailslot) has the following
  521. form:
  522. Timestamp of the alert event
  523. "USER"
  524. "SERVER"
  525. Message id of message
  526. Number of merge strings which will be substituted into message
  527. Merge strings, each separated by a zero terminator.
  528. Username
  529. \\ComputerNameOfUser
  530. Arguments:
  531. Alert - Supplies a pointer to the alert notification structure.
  532. Return Value:
  533. None.
  534. --*/
  535. {
  536. NET_API_STATUS status;
  537. PUSER_OTHER_INFO UserInfo = (PUSER_OTHER_INFO) ALERT_OTHER_INFO(Alert);
  538. DWORD i;
  539. LPTSTR MergeStrings;
  540. LPTSTR Username;
  541. LPTSTR ComputerNameOfUser;
  542. LPTSTR To = NULL;
  543. IF_DEBUG(FORMAT) {
  544. NetpKdPrint(("[Alerter] Got user alert\n"));
  545. }
  546. MergeStrings = (LPTSTR) ALERT_VAR_DATA(UserInfo);
  547. //
  548. // Name of user to be notified of the alert is found after the merge
  549. // strings.
  550. //
  551. for (Username = MergeStrings, i = 0; i < UserInfo->alrtus_numstrings; i++) {
  552. Username += STRLEN(Username) + 1;
  553. }
  554. //
  555. // Computer name of user is in the alert structure after the user name.
  556. //
  557. ComputerNameOfUser = Username + STRLEN(Username) + 1;
  558. //
  559. // If both username and computer name are not specified, cannot send the
  560. // message
  561. //
  562. if (*Username == TCHAR_EOS && *ComputerNameOfUser == TCHAR_EOS) {
  563. NetpKdPrint((
  564. "[Alerter] Alert not sent. Username or computername not specified.\n"
  565. ));
  566. return;
  567. }
  568. //
  569. // Setup the To pointer to point to the message alias that canonicalize
  570. // properly. If there's a problem with the username, we will send the
  571. // message to the computer name.
  572. //
  573. if (AlCanonicalizeMessageAlias(Username) == NERR_Success) {
  574. To = Username;
  575. }
  576. //
  577. // Computer name may or may not be preceeded by backslashes
  578. //
  579. if (*ComputerNameOfUser == TCHAR_BACKSLASH &&
  580. *(ComputerNameOfUser + 1) == TCHAR_BACKSLASH) {
  581. ComputerNameOfUser += 2;
  582. }
  583. if (AlCanonicalizeMessageAlias(ComputerNameOfUser) == NERR_Success &&
  584. To == NULL) {
  585. To = ComputerNameOfUser;
  586. }
  587. //
  588. // Both username and computer name are not acceptable.
  589. //
  590. if (To == NULL) {
  591. NetpKdPrint((
  592. "[Alerter] Alert not sent. Username & computername are not acceptable.\n"
  593. ));
  594. return;
  595. }
  596. status = AlMakeMessageHeader(
  597. Alert->alrt_servicename,
  598. To,
  599. APE2_ALERTER_USER_SUBJ,
  600. Alert->alrt_timestamp,
  601. FALSE // Not an admin alert
  602. );
  603. if (status != NERR_Success) {
  604. NetpKdPrint((
  605. "[Alerter] Alert not sent. Error making message header %lu\n",
  606. status
  607. ));
  608. return;
  609. }
  610. AlMakeMessageBody(
  611. UserInfo->alrtus_errcode,
  612. MergeStrings,
  613. UserInfo->alrtus_numstrings
  614. );
  615. //
  616. // Send the message
  617. //
  618. if (AlSendMessage(To) == NERR_Success) {
  619. return;
  620. }
  621. //
  622. // If To points to the Username and the send was not successful, try
  623. // sending to the computer name of user.
  624. //
  625. if (To == Username) {
  626. (void) AlSendMessage(ComputerNameOfUser);
  627. }
  628. }
  629. VOID
  630. AlPrintFormatAndSend(
  631. IN PSTD_ALERT Alert
  632. )
  633. /*++
  634. Routine Description:
  635. This function takes a print alert notification, formats it into an alert
  636. message, and sends it to the computer name which the print job submitter
  637. is on. If the print alert is for certain type of printing error, like
  638. the printer is offline, the admins on the alert names list gets the alert
  639. message as well.
  640. Arguments:
  641. Alert - Supplies a pointer to the alert notification structure.
  642. Return Value:
  643. None.
  644. --*/
  645. {
  646. PPRINT_OTHER_INFO PrintInfo = (PPRINT_OTHER_INFO) ALERT_OTHER_INFO(Alert);
  647. LPTSTR ComputerName = NULL;
  648. LPTSTR Username = NULL;
  649. LPTSTR DocumentName = NULL;
  650. LPTSTR PrinterName = NULL;
  651. LPTSTR ServerName = NULL;
  652. LPTSTR StatusString = NULL;
  653. DWORD PrintMessageID = APE2_ALERTER_PRINTING_SUCCESS;
  654. BOOL AdminAlso;
  655. LPTSTR AdminToAlert;
  656. IF_DEBUG(FORMAT) {
  657. NetpKdPrint(("[Alerter] Got print alert\n"));
  658. }
  659. ComputerName = (LPTSTR) ALERT_VAR_DATA(PrintInfo);
  660. Username = ComputerName + STRLEN(ComputerName) + 1;
  661. DocumentName = Username + STRLEN(Username) + 1;
  662. PrinterName = DocumentName + STRLEN(DocumentName) + 1;
  663. ServerName = PrinterName + STRLEN(PrinterName) + 1;
  664. StatusString = ServerName + STRLEN(ServerName) + 1;
  665. if ( ((PrintInfo->alrtpr_status & PRJOB_QSTATUS) == PRJOB_QS_PRINTING)
  666. && (PrintInfo->alrtpr_status & PRJOB_COMPLETE) ) {
  667. PrintMessageID = APE2_ALERTER_PRINTING_SUCCESS;
  668. }
  669. else {
  670. if ( *StatusString == '\0' ) {
  671. PrintMessageID = APE2_ALERTER_PRINTING_FAILURE;
  672. }
  673. else {
  674. PrintMessageID = APE2_ALERTER_PRINTING_FAILURE2;
  675. }
  676. }
  677. //
  678. // If error, notify admins on the alert names list also, besides the print
  679. // job submitter.
  680. //
  681. AdminAlso = (PrintInfo->alrtpr_status &
  682. (PRJOB_DESTOFFLINE | PRJOB_INTERV | PRJOB_ERROR));
  683. //
  684. // Computer name may or may not be preceeded by backslashes
  685. //
  686. if (*ComputerName == TCHAR_BACKSLASH &&
  687. *(ComputerName + 1) == TCHAR_BACKSLASH) {
  688. ComputerName += 2;
  689. }
  690. (VOID) AlCanonicalizeMessageAlias(ComputerName);
  691. //
  692. // Format message for the print job submitter
  693. //
  694. MessageBuffer[0] = AL_NULL_CHAR;
  695. AlMakePrintMessage( PrintMessageID,
  696. DocumentName,
  697. PrinterName,
  698. ServerName,
  699. StatusString );
  700. //
  701. // If the print job submitter is one of admins specified on the alert
  702. // names list, don't send the message to the submitter yet. All the
  703. // admins on alert names list will receive the message during admin
  704. // processing below.
  705. //
  706. if ((AdminAlso && !IsDuplicateReceiver(ComputerName) ) ||
  707. ! AdminAlso) {
  708. if ( AlSendMessage(ComputerName) != NERR_Success ) {
  709. (void) AlSendMessage(Username);
  710. }
  711. }
  712. //
  713. // We are done if this is not an admin related alert.
  714. //
  715. if (! AdminAlso) {
  716. return;
  717. }
  718. AdminToAlert = AlertNamesW;
  719. //
  720. // Send alert message to every admin on the alert names list.
  721. //
  722. while (AdminToAlert != NULL && *AdminToAlert != TCHAR_EOS) {
  723. MessageBuffer[0] = AL_NULL_CHAR;
  724. AlMakePrintMessage( PrintMessageID,
  725. DocumentName,
  726. PrinterName,
  727. ServerName,
  728. StatusString );
  729. //
  730. // Send message to the admin.
  731. //
  732. (void) AlSendMessage(AdminToAlert);
  733. AdminToAlert += (STRLEN(AdminToAlert) + 1);
  734. }
  735. }
  736. STATIC
  737. VOID
  738. AlMakePrintMessage(
  739. IN DWORD PrintMessageID,
  740. IN LPTSTR DocumentName,
  741. IN LPTSTR PrinterName,
  742. IN LPTSTR ServerName,
  743. IN LPTSTR StatusString
  744. )
  745. /*++
  746. Routine Description:
  747. PrintMessageID - ID of message string to use
  748. DocumentName, PrinterName, ServerName, StatusString(Optional) - insert strings passed by spooler.
  749. Return Value:
  750. None.
  751. --*/
  752. {
  753. LPSTR SubstituteStrings[4];
  754. SubstituteStrings[0] = NetpAllocStrFromWStr(DocumentName);
  755. if (SubstituteStrings[0] == NULL) {
  756. return;
  757. }
  758. SubstituteStrings[1] = NetpAllocStrFromWStr(PrinterName);
  759. if (SubstituteStrings[1] == NULL) {
  760. NetApiBufferFree(SubstituteStrings[0]);
  761. return;
  762. }
  763. SubstituteStrings[2] = NetpAllocStrFromWStr(ServerName);
  764. if (SubstituteStrings[2] == NULL) {
  765. NetApiBufferFree(SubstituteStrings[0]);
  766. NetApiBufferFree(SubstituteStrings[1]);
  767. return;
  768. }
  769. SubstituteStrings[3] = NULL;
  770. if ( *StatusString != '\0' ) {
  771. SubstituteStrings[3] = NetpAllocStrFromWStr(StatusString);
  772. if (SubstituteStrings[3] == NULL) {
  773. NetApiBufferFree(SubstituteStrings[0]);
  774. NetApiBufferFree(SubstituteStrings[1]);
  775. NetApiBufferFree(SubstituteStrings[2]);
  776. return;
  777. }
  778. }
  779. AlAppendMessage(
  780. PrintMessageID,
  781. MessageBuffer,
  782. SubstituteStrings,
  783. *StatusString == '\0' ? 3 : 4
  784. );
  785. NetApiBufferFree(SubstituteStrings[0]);
  786. NetApiBufferFree(SubstituteStrings[1]);
  787. NetApiBufferFree(SubstituteStrings[2]);
  788. if ( SubstituteStrings[3] )
  789. NetApiBufferFree(SubstituteStrings[3]);
  790. }
  791. STATIC
  792. NET_API_STATUS
  793. AlSendMessage(
  794. IN LPTSTR MessageAlias
  795. )
  796. /*++
  797. Routine Description:
  798. This function sends the message in MessageBuffer to the specified
  799. message alias. If an error occurs while sending the message, it will
  800. be logged to the error log file.
  801. Arguments:
  802. MessageAlias - Supplies the message alias of recipient of the message.
  803. Return Value:
  804. NET_API_STATUS - NERR_Success or reason for failure.
  805. --*/
  806. {
  807. static NET_API_STATUS PreviousStatus = NERR_Success;
  808. NET_API_STATUS Status;
  809. LPWSTR MessageW;
  810. DWORD MessageSize;
  811. MessageW = NetpAllocWStrFromStr(MessageBuffer);
  812. if (MessageW == NULL) {
  813. NetpKdPrint(("[Alerter] AlSendMessage: NetpAllocWStrFromStr failed\n"));
  814. return ERROR_NOT_ENOUGH_MEMORY;
  815. }
  816. // fixes alterts to DOS clients and garbage in NT to NT altert message
  817. MessageSize = wcslen( MessageW ) * sizeof(WCHAR) ;
  818. //
  819. // Send a directed message to the specified message alias
  820. //
  821. IF_DEBUG(FORMAT) {
  822. NetpKdPrint(("\n\nMessage To " FORMAT_LPTSTR "\n\n", MessageAlias));
  823. NetpDbgHexDump((LPBYTE) MessageW, MessageSize);
  824. }
  825. if ((Status = NetMessageBufferSend(
  826. NULL,
  827. MessageAlias,
  828. AlLocalComputerNameW,
  829. (LPBYTE) MessageW,
  830. MessageSize
  831. )) != NERR_Success) {
  832. NetpKdPrint(("[Alerter] Error sending message to "
  833. FORMAT_LPTSTR " %lu\n", MessageAlias, Status));
  834. if (Status != NERR_NameNotFound &&
  835. Status != NERR_BadReceive &&
  836. Status != NERR_UnknownServer &&
  837. Status != PreviousStatus) {
  838. AlHandleError(AlErrorSendMessage, Status, MessageAlias);
  839. PreviousStatus = Status;
  840. }
  841. }
  842. NetApiBufferFree(MessageW);
  843. return Status;
  844. }
  845. STATIC
  846. NET_API_STATUS
  847. AlAppendMessage(
  848. IN DWORD MessageId,
  849. OUT LPSTR MessageBuffer,
  850. IN LPSTR *SubstituteStrings,
  851. IN DWORD NumberOfSubstituteStrings
  852. )
  853. /*++
  854. Routine Description:
  855. This function gets the message, as specified by the message id, from a
  856. predetermined message file. It then appends the message to the
  857. MessageBuffer.
  858. Arguments:
  859. MessageId - Supplies the message id of message to get from message file.
  860. MessageBuffer - Supplies a pointer to the buffer which the message is
  861. appended to.
  862. SubstituteStrings - Supplies an array of strings to substitute into the
  863. message.
  864. NumberOfSubstituteStrings - Supplies the number of strings in array of
  865. SunstituteStrings
  866. Return Value:
  867. NET_API_STATUS - NERR_Success or reason for failure.
  868. --*/
  869. {
  870. WORD MessageLength = 0;
  871. NET_API_STATUS status;
  872. LPBYTE RetrievedMessage;
  873. LPBYTE ResultMessage = NULL;
  874. //
  875. // Allocate memory for the buffer which receives the message from the
  876. // message file.
  877. //
  878. if ((RetrievedMessage = (LPBYTE) LocalAlloc(
  879. 0,
  880. MAX_ALERTER_MESSAGE_SIZE+1
  881. )) == NULL) {
  882. return GetLastError();
  883. }
  884. if ((status = (NET_API_STATUS) DosGetMessage(
  885. SubstituteStrings,
  886. (WORD) NumberOfSubstituteStrings,
  887. RetrievedMessage,
  888. MAX_ALERTER_MESSAGE_SIZE,
  889. (WORD) MessageId,
  890. MESSAGE_FILENAME,
  891. &MessageLength
  892. )) != 0) {
  893. goto CleanUp;
  894. }
  895. RetrievedMessage[MessageLength] = AL_NULL_CHAR;
  896. //
  897. // Set the result message to the beginning of message
  898. // if there are no spaces in the message.
  899. //
  900. if (ResultMessage == NULL) {
  901. ResultMessage = RetrievedMessage;
  902. }
  903. strcat(MessageBuffer, ResultMessage);
  904. CleanUp:
  905. LocalFree(RetrievedMessage);
  906. return status;
  907. }
  908. STATIC
  909. BOOL
  910. IsDuplicateReceiver(
  911. LPTSTR Name
  912. )
  913. /*++
  914. Routine Description:
  915. This function compares the specified name with the names on the alert
  916. names list and returns TRUE if there is a match; FALSE otherwise.
  917. Arguments:
  918. Name - Supplies a name to compare with the alert names list.
  919. Return Value:
  920. TRUE if match any name; FALSE otherwise.
  921. --*/
  922. {
  923. LPTSTR AdminToAlert = AlertNamesW;
  924. while (AdminToAlert != NULL && *AdminToAlert != TCHAR_EOS) {
  925. if (STRICMP(Name, AdminToAlert) == 0) {
  926. return TRUE;
  927. }
  928. AdminToAlert = STRCHR(AdminToAlert, TCHAR_EOS);
  929. AdminToAlert++;
  930. }
  931. return FALSE;
  932. }
  933. VOID
  934. AlFormatErrorMessage(
  935. IN NET_API_STATUS Status,
  936. IN LPTSTR MessageAlias,
  937. OUT LPTSTR ErrorMessage,
  938. IN DWORD ErrorMessageBufferSize
  939. )
  940. /*++
  941. Routine Description:
  942. This function formats 3 strings and place them, NULL separated, into
  943. the supplied ErrorMessage buffer. The strings appear in the following
  944. order:
  945. Status
  946. MessageAlias
  947. Message which was not send
  948. Arguments:
  949. Status - Supplies the status code of the error.
  950. MessageAlias - Supplies the message alias of the intended recipient.
  951. ErrorMessage - Returns the formatted error message in this buffer.
  952. ErrorMessageBufferSize - Supplies the size of the error message buffer
  953. in bytes.
  954. Return Value:
  955. None.
  956. --*/
  957. {
  958. LPTSTR MessageBufferPointer;
  959. LPTSTR MBPtr;
  960. LPTSTR MBPtr2;
  961. DWORD SizeOfString;
  962. LPTSTR MessageBufferW;
  963. CHAR MessageBufferTmp[MAX_ALERTER_MESSAGE_SIZE];
  964. RtlZeroMemory(ErrorMessage, ErrorMessageBufferSize);
  965. //
  966. // Don't muck with the actual message buffer itself because it
  967. // may still be in use.
  968. //
  969. strcpy(MessageBufferTmp, MessageBuffer);
  970. //
  971. // Put status in error message buffer
  972. //
  973. ultow(Status, ErrorMessage, 10);
  974. MBPtr = &ErrorMessage[STRLEN(ErrorMessage) + 1];
  975. //
  976. // Put message alias in error message buffer
  977. //
  978. STRCPY(MBPtr, MessageAlias);
  979. MBPtr += (STRLEN(MessageAlias) + 1);
  980. //
  981. // Put the message that failed to send in error message buffer
  982. //
  983. MessageBufferW = NetpAllocWStrFromStr(MessageBufferTmp);
  984. if (MessageBufferW == NULL) {
  985. return;
  986. }
  987. MessageBufferPointer = MessageBufferW;
  988. while (MBPtr2 = STRCHR(MessageBufferPointer, AL_EOL_WCHAR)) {
  989. *MBPtr2++ = TCHAR_EOS;
  990. SizeOfString = (DWORD) ((LPBYTE)MBPtr2 - (LPBYTE)MessageBufferPointer) + sizeof(TCHAR);
  991. //
  992. // Check for error message buffer overflow
  993. //
  994. if ((LPBYTE)MBPtr - (LPBYTE)ErrorMessage + SizeOfString >=
  995. ErrorMessageBufferSize) {
  996. break;
  997. }
  998. STRCPY(MBPtr, MessageBufferPointer);
  999. STRCAT(MBPtr, AL_CRLF_STRING);
  1000. MBPtr += SizeOfString / sizeof(TCHAR);
  1001. MessageBufferPointer = MBPtr2;
  1002. }
  1003. if (((LPBYTE)MBPtr - (LPBYTE)ErrorMessage +
  1004. STRLEN(MessageBufferPointer) * sizeof(TCHAR)) >
  1005. ErrorMessageBufferSize) {
  1006. //
  1007. // Put as much info into the error message buffer as possible
  1008. //
  1009. STRNCPY(MBPtr,
  1010. MessageBufferPointer,
  1011. ErrorMessageBufferSize/sizeof(TCHAR) - (int)(MBPtr - ErrorMessage));
  1012. ErrorMessage[(ErrorMessageBufferSize/sizeof(TCHAR))-1] = TCHAR_EOS;
  1013. } else {
  1014. STRCPY(MBPtr, MessageBufferPointer);
  1015. }
  1016. NetApiBufferFree(MessageBufferW);
  1017. }
  1018. NET_API_STATUS
  1019. AlCanonicalizeMessageAlias(
  1020. LPTSTR MessageAlias
  1021. )
  1022. /*++
  1023. Routine Description:
  1024. This function canonicalizes the message alias by calling
  1025. I_NetNameCanonicalize.
  1026. Arguments:
  1027. MessageAlias - Supplies the message alias of an intended recipient for
  1028. the alert message.
  1029. Return Value:
  1030. NET_API_STATUS - NERR_Success or reason for failure.
  1031. --*/
  1032. {
  1033. NET_API_STATUS Status;
  1034. //
  1035. // Canonicalize message alias which will receive message
  1036. //
  1037. Status = I_NetNameCanonicalize(
  1038. NULL,
  1039. MessageAlias,
  1040. MessageAlias,
  1041. (STRLEN(MessageAlias) + 1) * sizeof(TCHAR),
  1042. NAMETYPE_USER,
  1043. 0
  1044. );
  1045. if (Status != NERR_Success) {
  1046. NetpKdPrint(("[Alerter] Error canonicalizing message alias " FORMAT_LPTSTR " %lu\n",
  1047. MessageAlias, Status));
  1048. AlHandleError(AlErrorSendMessage, Status, MessageAlias);
  1049. }
  1050. return Status;
  1051. }
  1052. VOID
  1053. AlMakeTimeString(
  1054. DWORD * Time,
  1055. PCHAR String,
  1056. int StringLength
  1057. )
  1058. /*++
  1059. Routine Description:
  1060. This function converts the UTC time expressed in seconds since 1/1/70
  1061. to an ASCII String.
  1062. Arguments:
  1063. Time - Pointer to the number of seconds since 1970 (UTC).
  1064. String - Pointer to the buffer to place the ASCII representation.
  1065. StringLength - The length of String in bytes.
  1066. Return Value:
  1067. None.
  1068. --*/
  1069. {
  1070. time_t LocalTime;
  1071. DWORD dwTimeTemp;
  1072. struct tm TmTemp;
  1073. SYSTEMTIME st;
  1074. int cchT=0, cchD;
  1075. NetpGmtTimeToLocalTime(*Time, &dwTimeTemp);
  1076. //
  1077. // Cast the DWORD returned by NetpGmtTimeToLocalTime up to
  1078. // a time_t. On 32-bit, this is a no-op. On 64-bit, this
  1079. // ensures the high DWORD of LocalTime is zeroed out.
  1080. //
  1081. LocalTime = (time_t) dwTimeTemp;
  1082. net_gmtime(&LocalTime, &TmTemp);
  1083. st.wYear = (WORD)(TmTemp.tm_year + 1900);
  1084. st.wMonth = (WORD)(TmTemp.tm_mon + 1);
  1085. st.wDay = (WORD)(TmTemp.tm_mday);
  1086. st.wHour = (WORD)(TmTemp.tm_hour);
  1087. st.wMinute = (WORD)(TmTemp.tm_min);
  1088. st.wSecond = (WORD)(TmTemp.tm_sec);
  1089. st.wMilliseconds = 0;
  1090. cchD = GetDateFormatA(GetThreadLocale(),
  1091. 0,
  1092. &st,
  1093. NULL,
  1094. String,
  1095. StringLength);
  1096. if (cchD != 0)
  1097. {
  1098. *(String + cchD - 1) = ' '; /* replace NULLC with blank */
  1099. cchT = GetTimeFormatA(GetThreadLocale(),
  1100. TIME_NOSECONDS,
  1101. &st,
  1102. NULL,
  1103. String + cchD,
  1104. StringLength - cchD);
  1105. if (cchT == 0)
  1106. {
  1107. //
  1108. // If this gets hit, MAX_DATE_TIME_LEN (in netapi\inc\timelib.h)
  1109. // needs to be increased
  1110. //
  1111. ASSERT(FALSE);
  1112. *(String + cchD - 1) = '\0';
  1113. }
  1114. }
  1115. return;
  1116. }