1 #include "elm_defs.h"
2 #include "elm_globals.h"
3 #include "sndparts.h"
4 #include "s_attach.h"
5 #include <assert.h>
6
7 #define AT_DUMMYMODE (user_level == 0)
8 #define AT_MAX_ATTACH 64
9
10 /* WARNING - side effects */
11 #define PutRight(line, str) PutLine0((line), COLS-(strlen(str)+2), (str))
12
13 #define S_(sel, str) catgets(elm_msg_cat, AttachSet, (sel), (str))
14
15 /*
16 * Locations within the screen display.
17 */
18 #define ATLINE_TITLE 0 /* page title */
19 #define ATLINE_LTOP 3 /* top line of att list */
20 #define ATLINE_LBOT (LINES-14) /* bot line of att list */
21 #define ATLINE_CURR_TITLE (LINES-12) /* current sel title */
22 #define ATLINE_CURR_FILE (LINES-11) /* curr filename */
23 #define ATLINE_CURR_TYPE (LINES-10) /* curr Content-Type: */
24 #define ATLINE_CURR_ENCOD (LINES-9) /* curr C-T-Encoding: */
25 #define ATLINE_CURR_DESCR (LINES-8) /* curr C-Description: */
26 #define ATLINE_CURR_DISP (LINES-7) /* curr C-Disposition: */
27 #define ATLINE_INSTR (LINES-4) /* instructions display */
28 #define ATLINE_PROMPT (LINES-2) /* user data entry line */
29
30 #define ATCOL_CURR_COLON 24 /* end of curr selection title */
31 #define ATCOL_CURR_DATA 26 /* start of curr selection data */
32
33 /*
34 * Display redraw requests.
35 */
36 #define ATDRAW_NONE (0) /* nothing to redisplay */
37 #define ATDRAW_HEADER (1<<0) /* redisplay the screen header */
38 #define ATDRAW_LIST (1<<1) /* redisplay list of attachments*/
39 #define ATDRAW_LIST_SEL (1<<2) /* just sel change of _LIST */
40 #define ATDRAW_CURR_FILE (1<<3) /* redisplay curr filename */
41 #define ATDRAW_CURR_TYPE (1<<4) /* redisplay curr Content-Type: */
42 #define ATDRAW_CURR_ENCOD (1<<5) /* redisplay curr C-T-Encoding: */
43 #define ATDRAW_CURR_DESCR (1<<6) /* redisplay curr C-Descrip: */
44 #define ATDRAW_CURR_DISP (1<<7) /* redisplay curr C-Disposition:*/
45 #define ATDRAW_CURR_ALL (ATDRAW_CURR_FILE|ATDRAW_CURR_TYPE|ATDRAW_CURR_ENCOD|ATDRAW_CURR_DESCR|ATDRAW_CURR_DISP)
46 #define ATDRAW_INSTR (1<<8)
47 #define ATDRAW_PROMPT (1<<9)
48 #define ATDRAW_ALL (~0) /* redisplay everything */
49
50
51 /*
52 * Entry selection/sizing/placment values.
53 */
54 #define AT_lines_per_page ((ATLINE_LBOT-ATLINE_LTOP) + 1)
55 #define AT_pagenum(sel) ((sel) / AT_lines_per_page)
56 #define AT_line(sel) ((sel) % AT_lines_per_page)
57 #define AT_first_sel_of_page (AT_pagenum(curr_sel)*AT_lines_per_page)
58 #define AT_last_sel_of_page ((AT_pagenum(curr_sel)+1)*AT_lines_per_page - 1)
59 #define AT_first_sel_of_list (0)
60 #define AT_last_sel_of_list (at_attachmenu_count - 1)
61 #define AT_num_pages (AT_last_sel_of_list/AT_lines_per_page + 1)
62
63 #define AT_FL_NOTOUCH (1<<0)
64 #define AT_FL_DUMMY (1<<1)
65 #define AT_FL_TAGGED (1<<2)
66
67 struct at_attachmenu_entry {
68 SEND_BODYPART *att;
69 int flags;
70 };
71
72 static struct at_attachmenu_entry at_attachmenu_list[AT_MAX_ATTACH];
73 static int at_attachmenu_count; /* num entries in at_attachmenu_list[] */
74 static SEND_BODYPART at_bogus; /* marker used in assertions */
75
76 static char at_acursor_on[] = "->"; /* arrow cursor on */
77 static char at_acursor_off[] = " "; /* arrow cursor blanked */
78
79 static void at_disp_entry P_((int, int, int));
80 static void at_disp_currline P_((int, const char *, const char *, int));
81 static void at_disp_instr P_((const char *, const char *, const char *, int));
82 static SEND_BODYPART *at_do_change_file P_((SEND_BODYPART *, int *, int *));
83 static int at_do_change_type P_((SEND_BODYPART *, int *));
84 static int at_do_change_encoding P_((SEND_BODYPART *, int *));
85 static int at_do_change_descrip P_((SEND_BODYPART *, int *));
86 static int at_do_change_disposition P_((SEND_BODYPART *, int *));
87
88 static char *strtruncate P_((char *, int));
89
90 /* manipulation of the internal attachment list */
91 static void atlist_initialize P_((void));
92 static int atlist_full P_((void));
93 static void atlist_insert P_((int, SEND_BODYPART *, int));
94 static void atlist_remove P_((int));
95 static void atlist_replace P_((int, SEND_BODYPART *, int));
96 static SEND_BODYPART *atlist_getbodypart P_((int));
97 static int atlist_getflags P_((int));
98 static int atlist_tagged_clrcnt P_((int));
99 static int atlist_tagged_move P_((int));
100
attachment_menu(user_attachments_p)101 PUBLIC int attachment_menu(user_attachments_p)
102 SEND_MULTIPART **user_attachments_p;
103 {
104 SEND_MULTIPART *mp;
105 SEND_BODYPART *att;
106 int done; /* TRUE when it's time to exit */
107 int inp_line, inp_col; /* cursor position for user input */
108 int do_redraw; /* what parts of screen need redrawing */
109 int bad_cmd; /* TRUE if last command was bad */
110 int cmd; /* command from user */
111 int next_cmd; /* force a "cmd" next time through loop */
112 int curr_sel; /* currently selected entry */
113 int prev_sel; /* selection at previous iteration */
114 int mssg_ok;
115 char *mssg_what;
116 int n, i;
117 char tmp_buf[SLEN], *s;
118
119 /* initialize our list of attachments */
120 atlist_initialize();
121 atlist_insert(0, bodypart_new(S_(AttachMainMessage,
122 "[main message]"), BP_IS_DUMMY), AT_FL_NOTOUCH|AT_FL_DUMMY);
123
124 /* slurp all of the existing attachments from an (SEND_BODYPART *) */
125 if (*user_attachments_p != NULL) {
126 for (;;) {
127 mp = multipart_next(*user_attachments_p, (SEND_MULTIPART *) NULL);
128 if (mp == NULL)
129 break;
130 if (atlist_full()) {
131 error(S_(AttachTooManyAttachments,
132 "Too many attachments for menu to handle!"));
133 return 0;
134 }
135 att = multipart_deletepart(*user_attachments_p, mp);
136 atlist_insert(at_attachmenu_count, att, 0);
137 }
138 multipart_destroy(*user_attachments_p);
139 *user_attachments_p = NULL;
140 }
141
142 done = FALSE;
143 do_redraw = ATDRAW_ALL;
144 bad_cmd = FALSE;
145 next_cmd = '\0';
146 prev_sel = curr_sel = 0;
147
148 while (!done) {
149
150 /* complain if last entry was bad */
151 if (bad_cmd) {
152 Beep();
153 bad_cmd = FALSE;
154 }
155
156 /* see if the selection moved */
157 if (prev_sel != curr_sel) {
158 if (curr_sel < AT_first_sel_of_list) {
159 /* adjust for movement beyond start of list */
160 if ((curr_sel = AT_first_sel_of_list) == prev_sel) {
161 bad_cmd = TRUE;
162 continue;
163 }
164 }
165 if (curr_sel > AT_last_sel_of_list) {
166 /* adjust for movement beyond end of list */
167 if ((curr_sel = AT_last_sel_of_list) == prev_sel) {
168 bad_cmd = TRUE;
169 continue;
170 }
171 }
172 if (AT_pagenum(curr_sel) != AT_pagenum(prev_sel)) {
173 /* page changed */
174 do_redraw = ATDRAW_ALL;
175 } else {
176 /* moved to a selection on this page */
177 do_redraw |= (ATDRAW_LIST_SEL|ATDRAW_CURR_ALL);
178 }
179 }
180
181 /* do screen updates */
182 if (do_redraw != ATDRAW_NONE) {
183
184 if (do_redraw == ATDRAW_ALL)
185 ClearScreen();
186
187 /* redraw the title and selection header lines */
188 if (do_redraw & ATDRAW_HEADER) {
189 if (do_redraw != ATDRAW_ALL)
190 ClearLine(ATLINE_TITLE);
191 CenterLine(ATLINE_TITLE, S_(AttachScreenTitle,
192 "Message Attachments Screen"));
193 sprintf(tmp_buf, S_(AttachScreenPage, "[page %d/%d]"),
194 AT_pagenum(curr_sel)+1, AT_num_pages);
195 PutLine0(ATLINE_TITLE, COLS-(strlen(tmp_buf)+2), tmp_buf);
196 }
197
198 /* redraw the entire list of entries */
199 if (do_redraw & ATDRAW_LIST) {
200 n = AT_first_sel_of_page;
201 for (i = 0 ; i < AT_lines_per_page ; ++i) {
202 if (n <= AT_last_sel_of_list) {
203 at_disp_entry(ATLINE_LTOP+i, n, (n == curr_sel));
204 } else if (do_redraw != ATDRAW_ALL) {
205 ClearLine(ATLINE_LTOP+i);
206 }
207 ++n;
208 }
209 }
210
211 /* redraw just the entries with selection changes */
212 if ((do_redraw & ATDRAW_LIST_SEL) && !(do_redraw & ATDRAW_LIST)) {
213 if (prev_sel >= AT_first_sel_of_page
214 && prev_sel <= AT_last_sel_of_page) {
215 if (arrow_cursor) {
216 PutLine0(ATLINE_LTOP+AT_line(prev_sel), 0,
217 at_acursor_off);
218 } else {
219 at_disp_entry(ATLINE_LTOP+AT_line(prev_sel),
220 prev_sel, FALSE);
221 }
222 }
223 if (arrow_cursor) {
224 PutLine0(ATLINE_LTOP+AT_line(curr_sel), 0, at_acursor_on);
225 } else {
226 at_disp_entry(ATLINE_LTOP+AT_line(curr_sel),
227 curr_sel, TRUE);
228 }
229 }
230
231 /* display details on the selected attachment */
232 if (do_redraw == ATDRAW_ALL) {
233 CenterLine(ATLINE_CURR_TITLE, S_(AttachCurrTitle,
234 "---------- Current Attachment ----------"));
235 }
236 att = atlist_getbodypart(curr_sel);
237 if (do_redraw & ATDRAW_CURR_FILE)
238 at_disp_currline(ATLINE_CURR_FILE,
239 S_(AttachCurrFileName, /*(*/ "F)ile Name"),
240 bodypart_get_filename(att),
241 (do_redraw != ATDRAW_ALL));
242 if (do_redraw & ATDRAW_CURR_TYPE)
243 at_disp_currline(ATLINE_CURR_TYPE,
244 S_(AttachCurrContType, /*(*/ "C)ontent Type"),
245 bodypart_get_content(att, BP_CONT_TYPE),
246 (do_redraw != ATDRAW_ALL));
247 if (do_redraw & ATDRAW_CURR_ENCOD)
248 at_disp_currline(ATLINE_CURR_ENCOD,
249 S_(AttachCurrContEncoding, /*(*/ "Content E)ncoding"),
250 bodypart_get_content(att, BP_CONT_ENCODING),
251 (do_redraw != ATDRAW_ALL));
252 if (do_redraw & ATDRAW_CURR_DESCR)
253 at_disp_currline(ATLINE_CURR_DESCR,
254 S_(AttachCurrContDescription, "Content De(s)cription"),
255 bodypart_get_content(att, BP_CONT_DESCRIPTION),
256 (do_redraw != ATDRAW_ALL));
257 if (do_redraw & ATDRAW_CURR_DISP)
258 at_disp_currline(ATLINE_CURR_DISP,
259 S_(AttachCurrContDisposition, "Content Dis(p)osition"),
260 bodypart_get_content(att, BP_CONT_DISPOSITION),
261 (do_redraw != ATDRAW_ALL));
262
263 if (do_redraw & ATDRAW_INSTR) {
264 at_disp_instr(
265 S_(AttachMainInstrNorm,
266 /*(((*/ "Use \"jk+-\" to move; \"?\" = help; a)dd, d)elete, or q)uit."),
267 S_(AttachMainInstrDummy1,
268 "Make selection: j/k = down/up, +/- = down/up page, or enter selection num."),
269 S_(AttachMainInstrDummy2, /*(((*/
270 "Then a)dd after or d)elete the selection. ? = help. Then q)uit when done."),
271 (do_redraw != ATDRAW_ALL));
272 }
273
274 if (do_redraw & ATDRAW_PROMPT) {
275 PutLine0(ATLINE_PROMPT, 0, S_(AttachMainPrompt, "Command: "));
276 GetCursorPos(&inp_line, &inp_col);
277 if (do_redraw != ATDRAW_ALL) {
278 /* erase down to (but EXCLUDING) the error line */
279 for (i = ATLINE_PROMPT+1 ; i < LINES ; ++i)
280 ClearLine(i);
281 }
282 }
283
284 if (do_redraw == ATDRAW_ALL)
285 show_last_error();
286 do_redraw = ATDRAW_NONE;
287
288 }
289
290 prev_sel = curr_sel;
291
292 /* prompt for command */
293 if (next_cmd != '\0') {
294 cmd = next_cmd;
295 next_cmd = '\0';
296 } else {
297 MoveCursor(inp_line, inp_col);
298 CleartoEOLN();
299 PutLine0(-1, -1, "q\b");
300 if ((cmd = GetKey(0)) == KEY_REDRAW) {
301 do_redraw = ATDRAW_ALL;
302 continue;
303 }
304 if (clear_error())
305 MoveCursor(inp_line, inp_col);
306 }
307
308 switch (cmd) {
309
310 case '?': /* help */
311 display_helpfile("attach");
312 do_redraw = ATDRAW_ALL;
313 break;
314
315 case ctrl('L'): /* redraw display */
316 do_redraw = ATDRAW_ALL;
317 break;
318
319 #ifdef ALLOW_SUBSHELL
320 case '!': /* subshell */
321 at_disp_instr((char *)NULL, (char *)NULL, (char *)NULL, TRUE);
322 ClearLine(ATLINE_PROMPT);
323 do_redraw = (subshell()
324 ? ATDRAW_ALL : (ATDRAW_INSTR|ATDRAW_PROMPT));
325 break;
326 #endif
327
328 case 'q': /* quit menu */
329 case '\r':
330 case '\n':
331 done = TRUE;
332 break;
333
334 case KEY_DOWN: /* down entry */
335 case 'j':
336 ++curr_sel;
337 break;
338
339 case KEY_UP: /* up entry */
340 case 'k':
341 --curr_sel;
342 break;
343
344 case KEY_NPAGE: /* down page */
345 case '+':
346 curr_sel += AT_lines_per_page;
347 break;
348
349 case KEY_PPAGE: /* up page */
350 case '-':
351 curr_sel -= AT_lines_per_page;
352 break;
353
354 case KEY_HOME: /* first entry */
355 curr_sel = 0;
356 break;
357
358 case KEY_END: /* last entry */
359 case '*':
360 curr_sel = AT_last_sel_of_list;
361 break;
362
363 case '1': case '2': case '3': /* numeric selection */
364 case '4': case '5': case '6':
365 case '7': case '8': case '9':
366 UnreadCh(cmd);
367 i = enter_number(ATLINE_PROMPT, curr_sel+1,
368 S_(AttachAttachment, "attachment")) - 1;
369 if (i < AT_first_sel_of_list || i > AT_last_sel_of_list) {
370 error(S_(AttachNoAttachmentThere,
371 "There isn't an attachment there!"));
372 } else if (i == curr_sel) {
373 error(S_(AttachSelectionNotChanged,
374 "Selection not changed."));
375 } else {
376 curr_sel = i;
377 }
378 do_redraw = ATDRAW_PROMPT;
379 break;
380
381 case 'a': /* add attachment */
382 if (atlist_full()) {
383 error(S_(AttachNoRoom,
384 "Sorry - no room for any more attachments."));
385 break;
386 }
387 att = bodypart_new(S_(AttachNewAttachment, "[new attachment]"),
388 BP_IS_DUMMY);
389 atlist_insert(++curr_sel, att, AT_FL_DUMMY);
390 do_redraw = ATDRAW_LIST;
391 /* we need to do a display update ... then continue with add */
392 next_cmd = (0x100|'a');
393 break;
394
395 case (0x100|'a'): /* add attachment ... continued */
396 att = at_do_change_file((SEND_BODYPART *)NULL, &do_redraw, &mssg_ok);
397 if (att == NULL) {
398 atlist_remove(curr_sel--);
399 if (mssg_ok) {
400 error(S_(AttachAttachmentNotAdded,
401 "Attachment not added."));
402 }
403 do_redraw |= ATDRAW_LIST;
404 break;
405 }
406 atlist_replace(curr_sel, att, 0);
407 if (mssg_ok) {
408 error(S_(AttachAttachmentHasBeenAdded,
409 "Attachment has been added to this message."));
410 }
411 do_redraw |= ATDRAW_LIST_SEL;
412 break;
413
414 case 'd': /* delete attachment */
415 if (atlist_getflags(curr_sel) & AT_FL_NOTOUCH) {
416 error(S_(AttachYouCantDelete, "Hey! You can't delete that!"));
417 break;
418 }
419 do_redraw = (ATDRAW_INSTR|ATDRAW_PROMPT);
420 if (!enter_yn(S_(AttachReallyDelete, "Really delete attachment?"),
421 FALSE, ATLINE_PROMPT, FALSE)) {
422 error(S_(AttachAttachmentNotDeleted,
423 "Attachment not deleted."));
424 break;
425 }
426 atlist_remove(curr_sel);
427 if (curr_sel > AT_last_sel_of_list)
428 --curr_sel;
429 error(S_(AttachAttachmentHasBeenDeleted,
430 "Attachment has been deleted from this message."));
431 do_redraw |= (ATDRAW_LIST|ATDRAW_CURR_ALL);
432 break;
433
434 case 'f': /* change "f)ile name" */
435 if (atlist_getflags(curr_sel) & AT_FL_NOTOUCH) {
436 error(S_(AttachYouCantChange, "Hey! You can't change that!"));
437 break;
438 }
439 att = at_do_change_file(atlist_getbodypart(curr_sel),
440 &do_redraw, &mssg_ok);
441 if (att == NULL) {
442 if (mssg_ok)
443 error(S_(AttachNotChanged, "Attachment not changed."));
444 } else {
445 atlist_replace(curr_sel, att, ~0);
446 if (mssg_ok) {
447 error(S_(AttachAttachmentFileChanged,
448 "Attachment file has been changed."));
449 }
450 do_redraw |= (ATDRAW_LIST|ATDRAW_CURR_ALL);
451 }
452 break;
453
454
455 case 'c': /* change "c)ontent type" */
456 if (atlist_getflags(curr_sel) & AT_FL_NOTOUCH) {
457 error(S_(AttachYouCantChange, "Hey! You can't change that!"));
458 break;
459 }
460 if (!at_do_change_type(atlist_getbodypart(curr_sel), &do_redraw))
461 error(S_(AttachNotChanged, "Attachment not changed."));
462 else {
463 error(S_(AttachAttachmentContTypeChanged,
464 "Attachment content type has been changed."));
465 do_redraw |= (ATDRAW_LIST_SEL|ATDRAW_CURR_ALL);
466 }
467 break;
468
469 case 'e': /* change "content e)ncoding" */
470 if (atlist_getflags(curr_sel) & AT_FL_NOTOUCH) {
471 error(S_(AttachYouCantChange, "Hey! You can't change that!"));
472 break;
473 }
474 if (!at_do_change_encoding(atlist_getbodypart(curr_sel), &do_redraw))
475 error(S_(AttachNotChanged, "Attachment not changed."));
476 else {
477 error(S_(AttachAttachmentContEncodingChanged,
478 "Attachment content encoding has been changed."));
479 do_redraw |= (ATDRAW_LIST_SEL|ATDRAW_CURR_ALL);
480 }
481 break;
482
483 case 's': /* change "content de(s)cription" */
484 if (atlist_getflags(curr_sel) & AT_FL_NOTOUCH) {
485 error(S_(AttachYouCantChange, "Hey! You can't change that!"));
486 break;
487 }
488 if (!at_do_change_descrip(atlist_getbodypart(curr_sel), &do_redraw))
489 error(S_(AttachNotChanged, "Attachment not changed."));
490 else {
491 error(S_(AttachAttachmentContDescriptionChanged,
492 "Attachment content description has been changed."));
493 do_redraw |= (ATDRAW_LIST_SEL|ATDRAW_CURR_ALL);
494 }
495 break;
496
497 case 'p': /* change "content dis(p)osition" */
498 if (atlist_getflags(curr_sel) & AT_FL_NOTOUCH) {
499 error(S_(AttachYouCantChange, "Hey! You can't change that!"));
500 break;
501 }
502 if (!at_do_change_disposition(atlist_getbodypart(curr_sel), &do_redraw))
503 error(S_(AttachNotChanged, "Attachment not changed."));
504 else {
505 error(S_(AttachAttachmentContDispositionChanged,
506 "Attachment content disposition has been changed."));
507 do_redraw |= (ATDRAW_LIST_SEL|ATDRAW_CURR_ALL);
508 }
509 break;
510
511 case 't': /* tag attachment */
512 if (atlist_getflags(curr_sel) & AT_FL_NOTOUCH) {
513 error(S_(AttachYouCantTag, "Hey! You can't tag that!"));
514 break;
515 }
516 i = (atlist_getflags(curr_sel) ^ AT_FL_TAGGED);
517 atlist_replace(curr_sel, (SEND_BODYPART *)NULL, i);
518 if (i & AT_FL_TAGGED) {
519 error(S_(AttachAttachmentHasBeenTagged,
520 "Current attachment has been tagged."));
521 } else {
522 error(S_(AttachTagHasBeenRemoved,
523 "Tag has been removed from current attachment."));
524 }
525 do_redraw |= ATDRAW_LIST_SEL;
526 break;
527
528 case ctrl('T'): /* remove all tags */
529 if (atlist_tagged_clrcnt(FALSE) == 0) {
530 error(S_(AttachTagsAlreadyClear,
531 "All tags already are cleared."));
532 break;
533 }
534 do_redraw = ATDRAW_PROMPT;
535 if (!enter_yn(S_(AttachReallyClearTags, "Really clear all tags?"),
536 FALSE, ATLINE_PROMPT, FALSE)) {
537 error(S_(AttachTagsNotChanged, "Tags not changed."));
538 break;
539 }
540 atlist_tagged_clrcnt(TRUE);
541 error(S_(AttachTagsHaveBeenCleared, "All tags have been cleared."));
542 do_redraw |= ATDRAW_LIST;
543 break;
544
545 case 'm': /* move tagged attachments */
546 if ((i = atlist_tagged_clrcnt(FALSE)) == 0) {
547 error(S_(AttachMustTagAttachments,
548 "You must tag the attachments you want to move."));
549 break;
550 }
551 do_redraw = ATDRAW_PROMPT;
552 s = (i > 1
553 ? S_(AttachMoveTaggedAttachments,
554 "Move tagged attachments after selection?")
555 : S_(AttachMoveTaggedAttachment,
556 "Move tagged attachment after selection?"));
557 if (!enter_yn(s, FALSE, ATLINE_PROMPT, FALSE)) {
558 if (i > 1) {
559 error(S_(AttachAttachmentsNotMoved,
560 "Attachments not moved."));
561 } else {
562 error(S_(AttachAttachmentNotMoved,
563 "Attachment not moved."));
564 }
565 break;
566 }
567 curr_sel = atlist_tagged_move(curr_sel);
568 if (i > 1) {
569 error(S_(AttachAttachmentsHaveBeenMoved,
570 "Attachments have been moved."));
571 } else {
572 error(S_(AttachAttachmentHaveBeenMoved,
573 "Attachment has been moved."));
574 }
575 do_redraw |= ATDRAW_LIST;
576 break;
577
578 default:
579 bad_cmd = TRUE;
580 break;
581
582 }
583
584 }
585
586 /* clear out entire bottom but the error line */
587 for (i = ATLINE_INSTR-1 ; i < LINES ; ++i)
588 ClearLine(i);
589
590 /* rebuild the user attachments list */
591 assert(*user_attachments_p == NULL);
592 for (i = 0 ; i < at_attachmenu_count ; ++i) {
593 if (*user_attachments_p == NULL)
594 *user_attachments_p = multipart_new((SEND_BODYPART *)NULL, 0L);
595 if ((atlist_getflags(i) & AT_FL_DUMMY)) {
596 bodypart_destroy(atlist_getbodypart(i));
597 } else {
598 multipart_appendpart(*user_attachments_p,
599 MULTIPART_TAIL(*user_attachments_p),
600 atlist_getbodypart(i), MP_ID_ATTACHMENT);
601 }
602 }
603
604 return 0;
605 }
606
607
at_disp_entry(line,n,selected)608 static void at_disp_entry(line, n, selected)
609 int line, n, selected;
610 {
611 int flags, len, i;
612 char out_buf[SLEN], trunc_buf[SLEN], *bp;
613 SEND_BODYPART *att;
614
615 att = atlist_getbodypart(n);
616 flags = atlist_getflags(n);
617
618 /* truncate parameters off displayed Content-Type */
619 (void) strfcpy(trunc_buf, bodypart_get_content(att, BP_CONT_TYPE), sizeof(trunc_buf));
620 for (bp = trunc_buf ; *bp != '\0' && *bp != ';' && !isspace(*bp) ; ++bp)
621 ;
622 *bp = '\0';
623
624 /* format up the line (sans Content-Description) */
625 (void) sprintf(out_buf, "%2.2s%1.1s%2d %-16.16s %-6.6s %-14.14s ",
626 (arrow_cursor && selected ? at_acursor_on : at_acursor_off),
627 ((flags & AT_FL_TAGGED) ? "+" : " "),
628 n+1,
629 basename(bodypart_get_filename(att)),
630 bodypart_get_content(att, BP_CONT_ENCODING),
631 trunc_buf);
632
633 /* truncate the Content-Description to fit the remainder of the line */
634 (void) strfcpy(trunc_buf, bodypart_get_content(att, BP_CONT_DESCRIPTION), sizeof(trunc_buf));
635 len = strlen(out_buf);
636 (void) strcpy(out_buf+len, strtruncate(trunc_buf, COLS - (len+2)));
637
638 MoveCursor(line, 0);
639 if (selected && !arrow_cursor)
640 StartStandout();
641 PutLine0(-1, -1, out_buf);
642 if (selected && !arrow_cursor) {
643 for (i = (COLS-2) - strlen(out_buf) ; i > 0 ; --i)
644 WriteChar(' ');
645 EndStandout();
646 } else {
647 CleartoEOLN();
648 }
649 }
650
651
at_disp_currline(line,title,value,do_erase)652 static void at_disp_currline(line, title, value, do_erase)
653 int line;
654 const char *title, *value;
655 int do_erase;
656 {
657 char trunc_buf[SLEN];
658
659 if (do_erase)
660 ClearLine(line);
661
662 PutLine0(line, ATCOL_CURR_COLON-(strlen(title)+1), title);
663 MoveCursor(line, ATCOL_CURR_COLON);
664 WriteChar(':');
665 (void) strfcpy(trunc_buf, value, sizeof(trunc_buf));
666 PutLine0(line, ATCOL_CURR_DATA,
667 strtruncate(trunc_buf, COLS-(ATCOL_CURR_DATA+2)));
668 }
669
670
at_disp_instr(instr_normal,instr_dummy1,instr_dummy2,do_erase)671 static void at_disp_instr(instr_normal, instr_dummy1, instr_dummy2, do_erase)
672 const char *instr_normal, *instr_dummy1, *instr_dummy2;
673 int do_erase;
674 {
675 if (AT_DUMMYMODE) {
676 if (do_erase)
677 ClearLine(ATLINE_INSTR-1);
678 if (instr_dummy1 != NULL && *instr_dummy1)
679 CenterLine(ATLINE_INSTR-1, instr_dummy1);
680 if (do_erase)
681 ClearLine(ATLINE_INSTR);
682 if (instr_dummy2 != NULL && *instr_dummy2)
683 CenterLine(ATLINE_INSTR, instr_dummy2);
684 } else {
685 if (do_erase) {
686 ClearLine(ATLINE_INSTR-1);
687 ClearLine(ATLINE_INSTR);
688 }
689 if (instr_normal != NULL && *instr_normal)
690 CenterLine(ATLINE_INSTR, instr_normal);
691 }
692 }
693
694
695 /* set "att" NULL to create a new attachment */
at_do_change_file(att,do_redraw_p,mssg_ok_p)696 static SEND_BODYPART *at_do_change_file(att, do_redraw_p, mssg_ok_p)
697 SEND_BODYPART *att;
698 int *do_redraw_p, *mssg_ok_p;
699 {
700 SEND_BODYPART *att_new;
701 char fname[SLEN], fb_dir[SLEN], fb_pat[SLEN], *s;
702 char cont_descrip[SLEN], cont_type[SLEN], cont_encoding[SLEN];
703 int do_browser, i;
704 static char save_fname[SLEN];
705
706 if (att == NULL)
707 fname[0] = '\0';
708 else
709 strfcpy(fname, bodypart_get_filename(att), sizeof(fname));
710
711 /*
712 * We may leave this routine with important messages on the display
713 * (e.g. from encoding failure). This flag tells the calling routine
714 * it is ok to write a message.
715 */
716 *mssg_ok_p = TRUE;
717
718 *do_redraw_p |= (ATDRAW_INSTR|ATDRAW_PROMPT|ATDRAW_CURR_FILE);
719 at_disp_instr(
720 S_(AttachChgFileInstrNorm,
721 "Enter attachment filename. \"~\", \"=\", and patterns ok, CTRL/D to abort."),
722 S_(AttachChgFileInstrDummy1,
723 "Enter name of file to attach to your mail message, or CTRL/D to abort entry."),
724 S_(AttachChgFileInstrDummy2,
725 "Say \".\" to select a file in the current directory."),
726 TRUE);
727 ClearLine(ATLINE_PROMPT);
728
729 for (att_new = NULL ; att_new == NULL ; ) {
730
731 if (*do_redraw_p == ATDRAW_ALL) {
732 /*
733 * We are in a bit of a jam here. We've been through this loop
734 * once before, and the display is garbage (probably from the
735 * browser). About all we can do is abort the add. There
736 * should be a message on the display explaining the problem,
737 * so be sure not to trounce it.
738 */
739 *mssg_ok_p = FALSE;
740 return (SEND_BODYPART *) NULL;
741 }
742
743 if (enter_string(fname, sizeof(fname),
744 ATLINE_CURR_FILE, ATCOL_CURR_DATA, ESTR_UPDATE) < 0)
745 return (SEND_BODYPART *) NULL;
746 clear_error();
747
748 /* FOO - I'd rather have "^R)ecall last" and ^B)rowse options above */
749 if (fname[0] == '\0') {
750 if (att != NULL)
751 (void) strfcpy(fname,
752 bodypart_get_filename(att), sizeof(fname));
753 else if (save_fname[0] != '\0')
754 (void) strfcpy(fname, save_fname, sizeof(fname));
755 if ((s = strrchr(fname, '/')) != NULL)
756 s[1] = '\0';
757 }
758
759 if (fname[0] == '\0') {
760 error("Please enter a filename for the attachment.");
761 continue;
762 }
763
764 if (!expand_filename(fname))
765 continue;
766
767 if (att != NULL && streq(fname, bodypart_get_filename(att)))
768 return (SEND_BODYPART *) NULL;
769
770 if (fbrowser_analyze_spec(fname, fb_dir, fb_pat)) {
771 *do_redraw_p = ATDRAW_ALL;
772 if (!fbrowser(fname, sizeof(fname), fb_dir, fb_pat,
773 FB_READ, "Select File to Attach")) {
774 return (SEND_BODYPART *) NULL;
775 }
776 }
777
778 if ((att_new = newpart_mimepart(fname)) != NULL) {
779 for (i = 0 ; i < BP_NUM_CONT_HEADERS ; ++i)
780 bodypart_guess_content(att_new, i);
781 }
782
783 }
784
785 (void) strfcpy(save_fname, fname, sizeof(save_fname));
786 *do_redraw_p |= ATDRAW_CURR_ALL;
787 return att_new;
788 }
789
790
at_do_change_type(att,do_redraw_p)791 static int at_do_change_type(att, do_redraw_p)
792 SEND_BODYPART *att;
793 int *do_redraw_p;
794 {
795 char cont_type[SLEN];
796
797 (void) strfcpy(cont_type, bodypart_get_content(att, BP_CONT_TYPE),
798 sizeof(cont_type));
799 *do_redraw_p |= (ATDRAW_INSTR|ATDRAW_PROMPT|ATDRAW_CURR_TYPE);
800 at_disp_instr(
801 S_(AttachChgTypeInstrNorm,
802 "Enter attachment content type, CTRL/D to abort."),
803 S_(AttachChgTypeInstrDummy1,
804 "Enter the \"content type\" of this file, or CTRL/D to abort entry."),
805 S_(AttachChgTypeInstrDummy2,
806 "You DO know what a Content-Type is...don't you???"), /*FOO*/
807 TRUE);
808 ClearLine(ATLINE_PROMPT);
809
810 for (;;) {
811 if (enter_string(cont_type, sizeof(cont_type),
812 ATLINE_CURR_TYPE, ATCOL_CURR_DATA, ESTR_UPDATE) < 0)
813 return FALSE;
814 if (streq(cont_type, bodypart_get_content(att, BP_CONT_TYPE)))
815 return FALSE;
816 clear_error();
817 if (cont_type[0] == '\0') {
818 error(S_(AttachChgTypeEnterType, "Please enter a content type."));
819 continue;
820 }
821 bodypart_set_content(att, BP_CONT_TYPE, cont_type);
822 return TRUE;
823 }
824 /*NOTREACHED*/
825 }
826
827
at_do_change_encoding(att,do_redraw_p)828 static int at_do_change_encoding(att, do_redraw_p)
829 SEND_BODYPART *att;
830 int *do_redraw_p;
831 {
832 char cont_encoding[SLEN];
833
834 (void) strfcpy(cont_encoding, bodypart_get_content(att, BP_CONT_ENCODING),
835 sizeof(cont_encoding));
836 *do_redraw_p |= (ATDRAW_INSTR|ATDRAW_PROMPT|ATDRAW_CURR_ENCOD);
837 at_disp_instr(
838 S_(AttachChgEncodingInstrNorm,
839 "Enter attachment content encoding, CTRL/D to abort."),
840 S_(AttachChgEncodingInstrDummy1,
841 "Enter the \"content encoding\" of this file, or CTRL/D to abort entry."),
842 S_(AttachChgEncodingInstrDummy2,
843 "Common values are \"7bit\", \"8bit\", \"base64\", and \"x-uuencode\"."),
844 TRUE);
845 ClearLine(ATLINE_PROMPT);
846
847 for (;;) {
848 if (enter_string(cont_encoding, sizeof(cont_encoding),
849 ATLINE_CURR_ENCOD, ATCOL_CURR_DATA, ESTR_UPDATE) < 0)
850 return FALSE;
851 if (streq(cont_encoding, bodypart_get_content(att, BP_CONT_ENCODING)))
852 return FALSE;
853 clear_error();
854 if (!encoding_is_reasonable(cont_encoding))
855 continue;
856 bodypart_set_content(att, BP_CONT_ENCODING, cont_encoding);
857 return TRUE;
858 }
859 /*NOTREACHED*/
860 }
861
862
at_do_change_descrip(att,do_redraw_p)863 static int at_do_change_descrip(att, do_redraw_p)
864 SEND_BODYPART *att;
865 int *do_redraw_p;
866 {
867 char cont_descrip[SLEN];
868
869 (void) strfcpy(cont_descrip, bodypart_get_content(att, BP_CONT_DESCRIPTION),
870 sizeof(cont_descrip));
871 *do_redraw_p |= (ATDRAW_INSTR|ATDRAW_PROMPT|ATDRAW_CURR_DESCR);
872 at_disp_instr(
873 S_(AttachChgDescripInstrNorm,
874 "Enter attachment content description, CTRL/D to abort."),
875 S_(AttachChgDescripInstrDummy1,
876 "Enter the \"content description\", or CTRL/D to abort entry."),
877 S_(AttachChgDescripInstrDummy2,
878 "This is just a brief comment to tell the recipient about the attachment."),
879 TRUE);
880 ClearLine(ATLINE_PROMPT);
881
882 for (;;) {
883 if (enter_string(cont_descrip, sizeof(cont_descrip),
884 ATLINE_CURR_DESCR, ATCOL_CURR_DATA, ESTR_UPDATE) < 0)
885 return FALSE;
886 if (streq(cont_descrip, bodypart_get_content(att, BP_CONT_DESCRIPTION)))
887 return FALSE;
888 clear_error();
889 bodypart_set_content(att, BP_CONT_DESCRIPTION, cont_descrip);
890 return TRUE;
891 }
892 /*NOTREACHED*/
893 }
894
895
at_do_change_disposition(att,do_redraw_p)896 static int at_do_change_disposition(att, do_redraw_p)
897 SEND_BODYPART *att;
898 int *do_redraw_p;
899 {
900 const char *cp;
901 char orig_fname[SLEN], new_fname[SLEN], *s;
902 int len;
903
904 *do_redraw_p |= (ATDRAW_INSTR|ATDRAW_PROMPT|ATDRAW_CURR_DISP);
905 at_disp_instr(
906 S_(AttachChgDispositionInstrNorm,
907 "Enter suggested filename (empty OK), CTRL/D to abort."),
908 S_(AttachChgDispositionInstrDummy1,
909 "Enter a suggested filename under which the recipient might want to save"),
910 S_(AttachChgDispositionInstrDummy2,
911 "this attachment (empty filename is OK), or CTRL/D to abort entry."),
912 TRUE);
913 ClearLine(ATLINE_PROMPT);
914
915 /*
916 * The Content-Disposition header is in a format:
917 * attachment; filename="/suggested/attachment/filename"
918 * The user will be editing just the filename. Here, we parse
919 * the filename out of the existing header.
920 */
921 orig_fname[0] = '\0';
922 cp = bodypart_get_content(att, BP_CONT_DISPOSITION);
923 while ((cp = strchr(cp, ';')) != NULL) {
924 ++cp; /* skip semicolon */
925 while (isspace(*cp)) /* advance to name */
926 ++cp;
927 if (!strbegConst(cp, "filename=")) /* "filename=" param? */
928 continue;
929 cp += (sizeof("filename=")-1); /* point to arg */
930 if (*cp == '"') {
931 /* filename is in quotes */
932 len = len_next_part(cp) - 2;
933 if (len >= sizeof(orig_fname))
934 len = sizeof(orig_fname)-1;
935 (void) strfcpy(orig_fname, cp+1, len+1);
936 } else {
937 /* filename is unquoted */
938 (void) strfcpy(orig_fname, cp, sizeof(orig_fname));
939 for (s = orig_fname ; *s != '\0' && !isspace(*s) ; ++s)
940 ;
941 *s = '\0';
942 }
943 }
944 (void) strcpy(new_fname, orig_fname);
945
946 for (;;) {
947 PutLine0(ATLINE_CURR_DISP, ATCOL_CURR_DATA, "attachment; filename=");
948 if (enter_string(new_fname, sizeof(new_fname), -1, -1, ESTR_UPDATE) < 0)
949 return FALSE;
950 if (streq(new_fname, orig_fname))
951 return FALSE;
952 clear_error();
953 if (new_fname[0] != '\0') {
954 sprintf(orig_fname, "attachment; filename=\"%s\"", new_fname);
955 bodypart_set_content(att, BP_CONT_DISPOSITION, orig_fname);
956 } else {
957 bodypart_set_content(att, BP_CONT_DISPOSITION, "attachment");
958 }
959 return TRUE;
960 }
961 /*NOTREACHED*/
962 }
963
964
strtruncate(str,len)965 static char *strtruncate(str, len)
966 char *str;
967 int len;
968 {
969 int i;
970
971 if (strlen(str) <= len)
972 return str;
973
974 while (len > 0 && isspace(str[len-1]))
975 --len;
976
977 /* see if there is a word boundary near the end */
978 for (i = 4 ; i < 10 ; ++i) {
979 if (isspace(str[len-i])) {
980 len = len-i+1;
981 while (len > 0 && isspace(str[len-1]))
982 --len;
983 (void) strcpy(str+len, " ...");
984 return str;
985 }
986 }
987
988 /* nope - just chop off the tail */
989 (void) strcpy(str+len-4, " ...");
990 return str;
991 }
992
993
atlist_initialize()994 static void atlist_initialize()
995 {
996 int i;
997
998 for (i = 0 ; i < AT_MAX_ATTACH ; ++i) {
999 at_attachmenu_list[i].att = &at_bogus;
1000 at_attachmenu_list[i].flags = (~0);
1001 }
1002 at_attachmenu_count = 0;
1003 }
1004
1005
atlist_full()1006 static int atlist_full()
1007 {
1008 return (at_attachmenu_count >= AT_MAX_ATTACH);
1009 }
1010
1011
atlist_insert(sel,att,flags)1012 static void atlist_insert(sel, att, flags)
1013 int sel;
1014 SEND_BODYPART *att;
1015 int flags;
1016 {
1017 int i;
1018
1019 assert(at_attachmenu_count >= 0 && at_attachmenu_count < AT_MAX_ATTACH);
1020 assert(sel >= 0 && sel <= at_attachmenu_count);
1021
1022 for (i = at_attachmenu_count ; i > sel ; --i) {
1023 assert(at_attachmenu_list[i-1].att != &at_bogus);
1024 at_attachmenu_list[i].att = at_attachmenu_list[i-1].att;
1025 at_attachmenu_list[i].flags = at_attachmenu_list[i-1].flags;
1026 }
1027 at_attachmenu_list[sel].att = att;
1028 at_attachmenu_list[sel].flags = flags;
1029 ++at_attachmenu_count;
1030 }
1031
1032
atlist_remove(sel)1033 static void atlist_remove(sel)
1034 int sel;
1035 {
1036 int i;
1037
1038 assert(at_attachmenu_count > 0 && at_attachmenu_count <= AT_MAX_ATTACH);
1039 assert(sel >= 0 && sel < at_attachmenu_count);
1040 assert(at_attachmenu_list[sel].att != &at_bogus);
1041
1042 bodypart_destroy(at_attachmenu_list[sel].att);
1043
1044 for (i = sel+1 ; i < at_attachmenu_count ; ++i) {
1045 assert(at_attachmenu_list[i].att != &at_bogus);
1046 at_attachmenu_list[i-1].att = at_attachmenu_list[i].att;
1047 at_attachmenu_list[i-1].flags = at_attachmenu_list[i].flags;
1048 }
1049 at_attachmenu_list[at_attachmenu_count].att = &at_bogus;
1050 at_attachmenu_list[at_attachmenu_count].flags = (~0);
1051 --at_attachmenu_count;
1052 }
1053
1054
atlist_replace(sel,att,flags)1055 static void atlist_replace(sel, att, flags)
1056 int sel;
1057 SEND_BODYPART *att;
1058 int flags;
1059 {
1060 assert(at_attachmenu_count > 0 && at_attachmenu_count <= AT_MAX_ATTACH);
1061 assert(sel >= 0 && sel < at_attachmenu_count);
1062 assert(at_attachmenu_list[sel].att != &at_bogus);
1063
1064 if (att != NULL && att != at_attachmenu_list[sel].att) {
1065 bodypart_destroy(at_attachmenu_list[sel].att);
1066 at_attachmenu_list[sel].att = att;
1067 }
1068 if (flags != ~0)
1069 at_attachmenu_list[sel].flags = flags;
1070 }
1071
1072
atlist_getbodypart(sel)1073 static SEND_BODYPART *atlist_getbodypart(sel)
1074 int sel;
1075 {
1076 assert(at_attachmenu_count > 0 && at_attachmenu_count <= AT_MAX_ATTACH);
1077 assert(sel >= 0 && sel < at_attachmenu_count);
1078 assert(at_attachmenu_list[sel].att != &at_bogus);
1079
1080 return at_attachmenu_list[sel].att;
1081 }
1082
1083
atlist_getflags(sel)1084 static int atlist_getflags(sel)
1085 int sel;
1086 {
1087 assert(at_attachmenu_count > 0 && at_attachmenu_count <= AT_MAX_ATTACH);
1088 assert(sel >= 0 && sel < at_attachmenu_count);
1089 assert(at_attachmenu_list[sel].att != &at_bogus);
1090
1091 return at_attachmenu_list[sel].flags;
1092 }
1093
1094
atlist_tagged_clrcnt(do_clear)1095 static int atlist_tagged_clrcnt(do_clear)
1096 int do_clear; /* if TRUE clear all tags, if FALSE just return count */
1097 {
1098 int count, i;
1099
1100 assert(at_attachmenu_count > 0 && at_attachmenu_count <= AT_MAX_ATTACH);
1101
1102 count = 0;
1103 for (i = 0 ; i < at_attachmenu_count ; ++i) {
1104 if (!(at_attachmenu_list[i].flags & AT_FL_NOTOUCH)
1105 && (at_attachmenu_list[i].flags & AT_FL_TAGGED)) {
1106 if (do_clear)
1107 at_attachmenu_list[i].flags &= ~AT_FL_TAGGED;
1108 ++count;
1109 }
1110 }
1111 return count;
1112 }
1113
1114
atlist_tagged_move(sel)1115 static int atlist_tagged_move(sel)
1116 int sel;
1117 {
1118 int nmoved, i, j;
1119 struct at_attachmenu_entry tmp_att;
1120
1121 assert(at_attachmenu_count > 0 && at_attachmenu_count <= AT_MAX_ATTACH);
1122 assert(sel >= 0 && sel < at_attachmenu_count);
1123
1124 nmoved = 0;
1125
1126 /*
1127 * move attachments left of selection:
1128 *
1129 * before: _a_ _b_ _c_ _d_ _e_ _f_ _g_ _h_
1130 * TAG SEL
1131 * i
1132 *
1133 * after: _a_ _b_ _d_ _e_ _f_ _c_ _g_ _h_
1134 * SEL TAG
1135 * i
1136 *
1137 * Several important effects:
1138 * - The move modifies the index of the selected attachment,
1139 * so the "sel" value must be adjusted.
1140 * - Adjustment is required or else the (++i) for the next
1141 * loop iteration will skip "_d_".
1142 * - The target of the next move should NOT be after "sel"
1143 * but rather "sel+1". Thus we need to track "nmoved".
1144 */
1145
1146 /* move attachments left of selection (note this modifies the sel value) */
1147 for (i = 0 ; i < sel ; ++i) {
1148 if (at_attachmenu_list[i].flags & AT_FL_NOTOUCH)
1149 continue;
1150 if (!(at_attachmenu_list[i].flags & AT_FL_TAGGED))
1151 continue;
1152 tmp_att.att = at_attachmenu_list[i].att;
1153 tmp_att.flags = at_attachmenu_list[i].flags;
1154 for (j = i ; j < sel+nmoved ; ++j) {
1155 at_attachmenu_list[j].att = at_attachmenu_list[j+1].att;
1156 at_attachmenu_list[j].flags = at_attachmenu_list[j+1].flags;
1157 }
1158 at_attachmenu_list[sel+nmoved].att = tmp_att.att;
1159 at_attachmenu_list[sel+nmoved].flags = tmp_att.flags;
1160 --sel;
1161 --i;
1162 ++nmoved;
1163 }
1164
1165 /*
1166 * move attachments right of selection:
1167 *
1168 * before: _a_ _b_ _c_ _d_ _e_ _f_ _g_ _h_
1169 * SEL TAG
1170 * i
1171 *
1172 * after: _a_ _b_ _c_ _f_ _d_ _e_ _g_ _h_
1173 * SEL TAG
1174 * i
1175 *
1176 * This is much simpler than the previous case. The only
1177 * side effect of concern is that making sure the target
1178 * index for subsequent moves is adjusted by "nmoved".
1179 */
1180
1181 for (i = sel+nmoved+1 ; i < at_attachmenu_count ; ++i) {
1182 if (at_attachmenu_list[i].flags & AT_FL_NOTOUCH)
1183 continue;
1184 if (!(at_attachmenu_list[i].flags & AT_FL_TAGGED))
1185 continue;
1186 tmp_att.att = at_attachmenu_list[i].att;
1187 tmp_att.flags = at_attachmenu_list[i].flags;
1188 for (j = i ; j > sel+nmoved+1 ; --j) {
1189 at_attachmenu_list[j].att = at_attachmenu_list[j-1].att;
1190 at_attachmenu_list[j].flags = at_attachmenu_list[j-1].flags;
1191 }
1192 at_attachmenu_list[sel+nmoved+1].att = tmp_att.att;
1193 at_attachmenu_list[sel+nmoved+1].flags = tmp_att.flags;
1194 ++nmoved;
1195 }
1196
1197 return sel;
1198 }
1199
1200