Merge branch 'master' of ssh://git.citadel.org/appl/gitroot/citadel
[citadel.git] / webcit / smtpqueue.c
1 /* 
2  * Display the outbound SMTP queue
3  */
4
5 #include "webcit.h"
6 HashList *QItemHandlers = NULL;
7
8 /*
9  * display one message in the queue
10  */
11 void display_queue_msg(long msgnum)
12 {
13         char buf[1024];
14         char keyword[32];
15         int in_body = 0;
16         int is_delivery_list = 0;
17         time_t submitted = 0;
18         time_t attempted = 0;
19         time_t last_attempt = 0;
20         int number_of_attempts = 0;
21         char sender[256];
22         char recipients[65536];
23         int recipients_len = 0;
24         char thisrecp[256];
25         char thisdsn[256];
26         char thismsg[512];
27         int thismsg_len;
28         long msgid = 0;
29         int len;
30
31         strcpy(sender, "");
32         strcpy(recipients, "");
33         recipients_len = 0;
34
35         serv_printf("MSG2 %ld", msgnum);
36         serv_getln(buf, sizeof buf);
37         if (buf[0] != '1') return;
38
39         while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
40
41                 if (!IsEmptyStr(buf)) {
42                         len = strlen(buf);
43                         if (buf[len - 1] == 13) {
44                                 buf[len - 1] = 0;
45                         }
46                 }
47
48                 if ( (IsEmptyStr(buf)) && (in_body == 0) ) {
49                         in_body = 1;
50                 }
51
52                 if ( (!in_body)
53                    && (!strncasecmp(buf, "Content-type: application/x-citadel-delivery-list", 49))
54                 ) {
55                         is_delivery_list = 1;
56                 }
57
58                 if ( (in_body) && (!is_delivery_list) ) {
59                         while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
60                                 /* Not a delivery list; flush and return quietly. */
61                         }
62                         return;
63                 }
64
65                 if ( (in_body) && (is_delivery_list) ) {
66                         extract_token(keyword, buf, 0, '|', sizeof keyword);
67
68                         if (!strcasecmp(keyword, "msgid")) {
69                                 msgid = extract_long(buf, 1);
70                         }
71
72                         if (!strcasecmp(keyword, "submitted")) {
73                                 submitted = extract_long(buf, 1);
74                         }
75
76                         if (!strcasecmp(keyword, "attempted")) {
77                                 attempted = extract_long(buf, 1);
78                                 ++number_of_attempts;
79                                 if (attempted > last_attempt) {
80                                         last_attempt = attempted;
81                                 }
82                         }
83
84                         if (!strcasecmp(keyword, "bounceto")) {
85                                 char *atsign;
86                                 extract_token(sender, buf, 1, '|', sizeof sender);
87
88                                 /* Strip off local hostname if it's our own */
89                                 atsign = strchr(sender, '@');
90                                 if (atsign != NULL) {
91                                         ++atsign;
92                                         if (!strcasecmp(atsign, ChrPtr(WC->serv_info->serv_nodename))) {
93                                                 --atsign;
94                                                 *atsign = 0;
95                                         }
96                                 }
97                         }
98
99                         if (!strcasecmp(keyword, "remote")) {
100                                 thismsg[0] = 0;
101
102                                 extract_token(thisrecp, buf, 1, '|', sizeof thisrecp);
103                                 extract_token(thisdsn, buf, 3, '|', sizeof thisdsn);
104
105                                 if (!IsEmptyStr(thisrecp)) {
106                                         stresc(thismsg, sizeof thismsg, thisrecp, 1, 1);
107                                         if (!IsEmptyStr(thisdsn)) {
108                                                 strcat(thismsg, "<br>&nbsp;&nbsp;<i>");
109                                                 stresc(&thismsg[strlen(thismsg)], sizeof thismsg,
110                                                         thisdsn, 1, 1);
111                                                 strcat(thismsg, "</i>");
112                                         }
113                                         thismsg_len = strlen(thismsg);
114
115                                         if ((recipients_len + thismsg_len + 100) < sizeof recipients) {
116                                                 if (!IsEmptyStr(recipients)) {
117                                                         strcpy(&recipients[recipients_len], "<br>");
118                                                         recipients_len += 6;
119                                                 }
120                                                 strcpy(&recipients[recipients_len], thismsg);
121                                                 recipients_len += thismsg_len;
122                                         }
123                                 }
124
125                         }
126
127                 }
128
129         }
130
131         wc_printf("<tr><td>");
132         wc_printf("%ld<br>", msgnum);
133         wc_printf(" <a href=\"javascript:DeleteSMTPqueueMsg(%ld,%ld);\">%s</a>", 
134                 msgnum, msgid, _("(Delete)")
135         );
136
137         wc_printf("</td><td>");
138         if (submitted > 0) {
139                 webcit_fmt_date(buf, 1024, submitted, 1);
140                 wc_printf("%s", buf);
141         }
142         else {
143                 wc_printf("&nbsp;");
144         }
145
146         wc_printf("</td><td>");
147         if (last_attempt > 0) {
148                 webcit_fmt_date(buf, 1024, last_attempt, 1);
149                 wc_printf("%s", buf);
150         }
151         else {
152                 wc_printf("&nbsp;");
153         }
154
155         wc_printf("</td><td>");
156         escputs(sender);
157
158         wc_printf("</td><td>");
159         wc_printf("%s", recipients);
160         wc_printf("</td></tr>\n");
161
162 }
163
164
165 typedef struct _mailq_entry {
166         StrBuf *Recipient;
167         StrBuf *StatusMessage;
168         int Status;
169         /**<
170          * 0 = No delivery has yet been attempted
171          * 2 = Delivery was successful
172          * 3 = Transient error like connection problem. Try next remote if available.
173          * 4 = A transient error was experienced ... try again later
174          * 5 = Delivery to this address failed permanently.  The error message
175          *     should be placed in the fourth field so that a bounce message may
176          *     be generated.
177          */
178
179         int n;
180         int Active;
181 }MailQEntry;
182
183 typedef struct queueitem {
184         long MessageID;
185         long QueMsgID;
186         long Submitted;
187         int FailNow;
188         HashList *MailQEntries;
189 /* copy of the currently parsed item in the MailQEntries list;
190  * if null add a new one.
191  */
192         MailQEntry *Current;
193         time_t ReattemptWhen;
194         time_t Retry;
195
196         long ActiveDeliveries;
197         StrBuf *EnvelopeFrom;
198         StrBuf *BounceTo;
199         StrBuf *SenderRoom;
200         ParsedURL *URL;
201         ParsedURL *FallBackHost;
202 } OneQueItem;
203
204
205 typedef void (*QItemHandler)(OneQueItem *Item, StrBuf *Line, const char **Pos);
206
207 typedef struct __QItemHandlerStruct {
208         QItemHandler H;
209 } QItemHandlerStruct;
210
211 void RegisterQItemHandler(const char *Key, long Len, QItemHandler H)
212 {
213         QItemHandlerStruct *HS = (QItemHandlerStruct*)malloc(sizeof(QItemHandlerStruct));
214         HS->H = H;
215         Put(QItemHandlers, Key, Len, HS, NULL);
216 }
217
218 void FreeMailQEntry(void *qv)
219 {
220         MailQEntry *Q = qv;
221         FreeStrBuf(&Q->Recipient);
222         FreeStrBuf(&Q->StatusMessage);
223         free(Q);
224 }
225 void FreeQueItem(OneQueItem **Item)
226 {
227         DeleteHash(&(*Item)->MailQEntries);
228         FreeStrBuf(&(*Item)->EnvelopeFrom);
229         FreeStrBuf(&(*Item)->BounceTo);
230         FreeStrBuf(&(*Item)->SenderRoom);
231         FreeURL(&(*Item)->URL);
232         free(*Item);
233         Item = NULL;
234 }
235 void HFreeQueItem(void *Item)
236 {
237         FreeQueItem((OneQueItem**)&Item);
238 }
239
240
241 OneQueItem *DeserializeQueueItem(StrBuf *RawQItem, long QueMsgID)
242 {
243         OneQueItem *Item;
244         const char *pLine = NULL;
245         StrBuf *Line;
246         StrBuf *Token;
247         
248         Item = (OneQueItem*)malloc(sizeof(OneQueItem));
249         memset(Item, 0, sizeof(OneQueItem));
250         Item->Retry = 0;
251         Item->MessageID = -1;
252         Item->QueMsgID = QueMsgID;
253
254         Token = NewStrBuf();
255         Line = NewStrBufPlain(NULL, 128);
256         while (pLine != StrBufNOTNULL) {
257                 const char *pItemPart = NULL;
258                 void *vHandler;
259
260                 StrBufExtract_NextToken(Line, RawQItem, &pLine, '\n');
261                 if (StrLength(Line) == 0) continue;
262                 StrBufExtract_NextToken(Token, Line, &pItemPart, '|');
263                 if (GetHash(QItemHandlers, SKEY(Token), &vHandler))
264                 {
265                         QItemHandlerStruct *HS;
266                         HS = (QItemHandlerStruct*) vHandler;
267                         HS->H(Item, Line, &pItemPart);
268                 }
269         }
270         FreeStrBuf(&Line);
271         FreeStrBuf(&Token);
272 /*
273         Put(ActiveQItems,
274             LKEY(Item->MessageID),
275             Item,
276             HFreeQueItem);
277 */      
278
279         return Item;
280 }
281
282 void tmplput_MailQID(StrBuf *Target, WCTemplputParams *TP)
283 {
284         OneQueItem *Item = (OneQueItem*) CTX;
285         StrBufAppendPrintf(Target, "%ld", Item->QueMsgID);;
286 }
287 void tmplput_MailQPayloadID(StrBuf *Target, WCTemplputParams *TP)
288 {
289         OneQueItem *Item = (OneQueItem*) CTX;
290         StrBufAppendPrintf(Target, "%ld", Item->MessageID);
291 }
292 void tmplput_MailQBounceTo(StrBuf *Target, WCTemplputParams *TP)
293 {
294         OneQueItem *Item = (OneQueItem*) CTX;
295         StrBufAppendTemplate(Target, TP, Item->BounceTo, 0);
296 }
297 void tmplput_MailQAttempted(StrBuf *Target, WCTemplputParams *TP)
298 {
299         char datebuf[64];
300         OneQueItem *Item = (OneQueItem*) CTX;
301         webcit_fmt_date(datebuf, 64, Item->ReattemptWhen, DATEFMT_BRIEF);
302         StrBufAppendBufPlain(Target, datebuf, -1, 0);
303 }
304 void tmplput_MailQSubmitted(StrBuf *Target, WCTemplputParams *TP)
305 {
306         char datebuf[64];
307         OneQueItem *Item = (OneQueItem*) CTX;
308         webcit_fmt_date(datebuf, 64, Item->Submitted, DATEFMT_BRIEF);
309         StrBufAppendBufPlain(Target, datebuf, -1, 0);
310 }
311 void tmplput_MailQEnvelopeFrom(StrBuf *Target, WCTemplputParams *TP)
312 {
313         OneQueItem *Item = (OneQueItem*) CTX;
314         StrBufAppendTemplate(Target, TP, Item->EnvelopeFrom, 0);
315 }
316 void tmplput_MailQSourceRoom(StrBuf *Target, WCTemplputParams *TP)
317 {
318         OneQueItem *Item = (OneQueItem*) CTX;
319         StrBufAppendTemplate(Target, TP, Item->SenderRoom, 0);
320 }
321
322 int Conditional_MailQ_HaveSourceRoom(StrBuf *Target, WCTemplputParams *TP)
323 {
324         OneQueItem *Item = (OneQueItem*) CTX;
325         return StrLength(Item->SenderRoom) > 0;
326 }
327
328 void tmplput_MailQRetry(StrBuf *Target, WCTemplputParams *TP)
329 {
330         char datebuf[64];
331         OneQueItem *Item = (OneQueItem*) CTX;
332
333         if (Item->Retry == 0) {
334                 StrBufAppendBufPlain(Target, _("First Attempt pending"), -1, 0);
335         }
336         else {
337                 webcit_fmt_date(datebuf, sizeof(datebuf), Item->Retry, DATEFMT_BRIEF);
338                 StrBufAppendBufPlain(Target, datebuf, -1, 0);
339         }
340 }
341
342 void tmplput_MailQRCPT(StrBuf *Target, WCTemplputParams *TP)
343 {
344         MailQEntry *Entry = (MailQEntry*) CTX;
345         StrBufAppendTemplate(Target, TP, Entry->Recipient, 0);
346 }
347 void tmplput_MailQRCPTStatus(StrBuf *Target, WCTemplputParams *TP)
348 {
349         MailQEntry *Entry = (MailQEntry*) CTX;
350         StrBufAppendPrintf(Target, "%ld", Entry->Status);
351 }
352 void tmplput_MailQStatusMsg(StrBuf *Target, WCTemplputParams *TP)
353 {
354         MailQEntry *Entry = (MailQEntry*) CTX;
355         StrBufAppendTemplate(Target, TP, Entry->StatusMessage, 0);
356 }
357
358 HashList *iterate_get_Recipients(StrBuf *Target, WCTemplputParams *TP)
359 {
360         OneQueItem *Item = (OneQueItem*) CTX;
361         return Item->MailQEntries;
362 }
363
364
365 void NewMailQEntry(OneQueItem *Item)
366 {
367         Item->Current = (MailQEntry*) malloc(sizeof(MailQEntry));
368         memset(Item->Current, 0, sizeof(MailQEntry));
369
370         if (Item->MailQEntries == NULL)
371                 Item->MailQEntries = NewHash(1, Flathash);
372         Item->Current->StatusMessage = NewStrBuf();
373         Item->Current->n = GetCount(Item->MailQEntries);
374         Put(Item->MailQEntries,
375             IKEY(Item->Current->n),
376             Item->Current,
377             FreeMailQEntry);
378 }
379
380 void QItem_Handle_MsgID(OneQueItem *Item, StrBuf *Line, const char **Pos)
381 {
382         Item->MessageID = StrBufExtractNext_long(Line, Pos, '|');
383 }
384
385 void QItem_Handle_EnvelopeFrom(OneQueItem *Item, StrBuf *Line, const char **Pos)
386 {
387         if (Item->EnvelopeFrom == NULL)
388                 Item->EnvelopeFrom = NewStrBufPlain(NULL, StrLength(Line));
389         StrBufExtract_NextToken(Item->EnvelopeFrom, Line, Pos, '|');
390 }
391
392 void QItem_Handle_BounceTo(OneQueItem *Item, StrBuf *Line, const char **Pos)
393 {
394         if (Item->BounceTo == NULL)
395                 Item->BounceTo = NewStrBufPlain(NULL, StrLength(Line));
396         StrBufExtract_NextToken(Item->BounceTo, Line, Pos, '|');
397 }
398
399 void QItem_Handle_SenderRoom(OneQueItem *Item, StrBuf *Line, const char **Pos)
400 {
401         if (Item->SenderRoom == NULL)
402                 Item->SenderRoom = NewStrBufPlain(NULL, StrLength(Line));
403         StrBufExtract_NextToken(Item->SenderRoom, Line, Pos, '|');
404 }
405
406 void QItem_Handle_Recipient(OneQueItem *Item, StrBuf *Line, const char **Pos)
407 {
408         if (Item->Current == NULL)
409                 NewMailQEntry(Item);
410         if (Item->Current->Recipient == NULL)
411                 Item->Current->Recipient=NewStrBufPlain(NULL, StrLength(Line));
412         StrBufExtract_NextToken(Item->Current->Recipient, Line, Pos, '|');
413         Item->Current->Status = StrBufExtractNext_int(Line, Pos, '|');
414         StrBufExtract_NextToken(Item->Current->StatusMessage, Line, Pos, '|');
415         Item->Current = NULL; // TODO: is this always right?
416 }
417
418
419 void QItem_Handle_retry(OneQueItem *Item, StrBuf *Line, const char **Pos)
420 {
421         Item->Retry = StrBufExtractNext_int(Line, Pos, '|');
422 }
423
424
425 void QItem_Handle_Submitted(OneQueItem *Item, StrBuf *Line, const char **Pos)
426 {
427         Item->Submitted = atol(*Pos);
428
429 }
430
431 void QItem_Handle_Attempted(OneQueItem *Item, StrBuf *Line, const char **Pos)
432 {
433         Item->ReattemptWhen = StrBufExtractNext_int(Line, Pos, '|');
434 }
435
436
437
438
439
440
441
442
443 void render_QUEUE(wc_mime_attachment *Mime, StrBuf *RawData, StrBuf *FoundCharset)
444 {
445         WCTemplputParams SubTP;
446
447         memset(&SubTP, 0, sizeof(WCTemplputParams));
448         SubTP.Filter.ContextType = CTX_MAILQITEM;
449         SubTP.Context = DeserializeQueueItem(Mime->Data, Mime->msgnum);
450         DoTemplate(HKEY("view_mailq_message"),NULL, &SubTP);
451         FreeQueItem ((OneQueItem**)&SubTP.Context);
452 }
453
454 void
455 ServerShutdownModule_SMTP_QUEUE
456 (void)
457 {
458         DeleteHash(&QItemHandlers);
459 }
460 void
461 ServerStartModule_SMTP_QUEUE
462 (void)
463 {
464         QItemHandlers = NewHash(0, NULL);
465 }
466
467 int qview_PrintPageHeader(SharedMessageStatus *Stat, void **ViewSpecific)
468 {
469         output_headers(1, 1, 1, 0, 0, 0);
470         return 0;
471 }
472
473 int qview_GetParamsGetServerCall(SharedMessageStatus *Stat,
474                                  void **ViewSpecific,
475                                  long oper,
476                                  char *cmd,
477                                  long len,
478                                  char *filter,
479                                  long flen)
480 {
481         if (!WC->is_aide)
482         {
483                 DoTemplate(HKEY("aide_required"), NULL, NULL);
484                 end_burst();
485
486                 return 300;
487         }
488         else {
489                 snprintf(cmd, len, "MSGS ALL|0|1");
490                 snprintf(filter, flen, "SUBJ|QMSG");
491                 DoTemplate(HKEY("view_mailq_header"), NULL, NULL);
492                 return 200;
493         }
494 }
495
496 /*
497  * Display task view
498  */
499 int qview_LoadMsgFromServer(SharedMessageStatus *Stat, 
500                             void **ViewSpecific, 
501                             message_summary* Msg, 
502                             int is_new, 
503                             int i)
504 {
505         wcsession *WCC = WC;
506         const StrBuf *Mime;
507
508         /* Not (yet?) needed here? calview *c = (calview *) *ViewSpecific; */
509         read_message(WCC->WBuf, HKEY("view_mailq_message_bearer"), Msg->msgnum, NULL, &Mime);
510
511         return 0;
512 }
513
514
515 int qview_RenderView_or_Tail(SharedMessageStatus *Stat, 
516                              void **ViewSpecific, 
517                              long oper)
518 {
519         wcsession *WCC = WC;
520         WCTemplputParams SubTP;
521
522         if (GetCount(WCC->summ) == 0)
523                 DoTemplate(HKEY("view_mailq_footer_empty"),NULL, &SubTP);
524         else
525                 DoTemplate(HKEY("view_mailq_footer"),NULL, &SubTP);
526         
527         return 0;
528 }
529 int qview_Cleanup(void **ViewSpecific)
530 {
531         wDumpContent(1);
532         return 0;
533 }
534
535 void 
536 InitModule_SMTP_QUEUE
537 (void)
538 {
539
540         RegisterQItemHandler(HKEY("msgid"),             QItem_Handle_MsgID);
541         RegisterQItemHandler(HKEY("envelope_from"),     QItem_Handle_EnvelopeFrom);
542         RegisterQItemHandler(HKEY("retry"),             QItem_Handle_retry);
543         RegisterQItemHandler(HKEY("attempted"),         QItem_Handle_Attempted);
544         RegisterQItemHandler(HKEY("remote"),            QItem_Handle_Recipient);
545         RegisterQItemHandler(HKEY("bounceto"),          QItem_Handle_BounceTo);
546         RegisterQItemHandler(HKEY("source_room"),       QItem_Handle_SenderRoom);
547         RegisterQItemHandler(HKEY("submitted"),         QItem_Handle_Submitted);
548         RegisterMimeRenderer(HKEY("application/x-citadel-delivery-list"), render_QUEUE, 1, 9000);
549         RegisterNamespace("MAILQ:ID", 0, 0, tmplput_MailQID, NULL, CTX_MAILQITEM);
550         RegisterNamespace("MAILQ:PAYLOAD:ID", 0, 0, tmplput_MailQPayloadID, NULL, CTX_MAILQITEM);
551         RegisterNamespace("MAILQ:BOUNCETO", 0, 1, tmplput_MailQBounceTo, NULL, CTX_MAILQITEM);
552         RegisterNamespace("MAILQ:ATTEMPTED", 0, 0, tmplput_MailQAttempted, NULL, CTX_MAILQITEM);
553         RegisterNamespace("MAILQ:SUBMITTED", 0, 0, tmplput_MailQSubmitted, NULL, CTX_MAILQITEM);
554         RegisterNamespace("MAILQ:ENVELOPEFROM", 0, 1, tmplput_MailQEnvelopeFrom, NULL, CTX_MAILQITEM);
555         RegisterNamespace("MAILQ:SRCROOM", 0, 1, tmplput_MailQSourceRoom, NULL, CTX_MAILQITEM);
556         RegisterConditional(HKEY("COND:MAILQ:HAVESRCROOM"), 0, Conditional_MailQ_HaveSourceRoom,  CTX_MAILQITEM);
557         RegisterNamespace("MAILQ:RETRY", 0, 0, tmplput_MailQRetry, NULL, CTX_MAILQITEM);
558
559         RegisterNamespace("MAILQ:RCPT:ADDR", 0, 1, tmplput_MailQRCPT, NULL, CTX_MAILQ_RCPT);
560         RegisterNamespace("MAILQ:RCPT:STATUS", 0, 0, tmplput_MailQRCPTStatus, NULL, CTX_MAILQ_RCPT);
561         RegisterNamespace("MAILQ:RCPT:STATUSMSG", 0, 1, tmplput_MailQStatusMsg, NULL, CTX_MAILQ_RCPT);
562
563         RegisterIterator("MAILQ:RCPT", 0, NULL, iterate_get_Recipients, 
564                          NULL, NULL, CTX_MAILQ_RCPT, CTX_MAILQITEM, IT_NOFLAG);
565
566
567         RegisterReadLoopHandlerset(
568                 VIEW_QUEUE,
569                 qview_GetParamsGetServerCall,
570                 qview_PrintPageHeader,
571                 NULL, /* TODO: is this right? */
572                 NULL,
573                 qview_LoadMsgFromServer,
574                 qview_RenderView_or_Tail,
575                 qview_Cleanup);
576
577 }