root/compose.c

Revision 5443:8b783c916893, 32.7 kB (checked in by Rocco Rutte <pdmef@…>, 3 months ago)

Use realpath() in mutt_pretty_mailbox() for paths that may need it.
This requires to add the buffer size as parameter since the result may
be longer than the original but still fit in the buffer. Closes #2948.

Line 
1/*
2 * Copyright (C) 1996-2000 Michael R. Elkins <me@mutt.org>
3 * Copyright (C) 2004 g10 Code GmbH
4 *
5 *     This program is free software; you can redistribute it and/or modify
6 *     it under the terms of the GNU General Public License as published by
7 *     the Free Software Foundation; either version 2 of the License, or
8 *     (at your option) any later version.
9 *
10 *     This program is distributed in the hope that it will be useful,
11 *     but WITHOUT ANY WARRANTY; without even the implied warranty of
12 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 *     GNU General Public License for more details.
14 *
15 *     You should have received a copy of the GNU General Public License
16 *     along with this program; if not, write to the Free Software
17 *     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
18 */ 
19
20#if HAVE_CONFIG_H
21# include "config.h"
22#endif
23
24#include "mutt.h"
25#include "mutt_curses.h"
26#include "mutt_idna.h"
27#include "mutt_menu.h"
28#include "rfc1524.h"
29#include "mime.h"
30#include "attach.h"
31#include "mapping.h"
32#include "mailbox.h"
33#include "sort.h"
34#include "charset.h"
35
36#ifdef MIXMASTER
37#include "remailer.h"
38#endif
39
40#include <errno.h>
41#include <string.h>
42#include <sys/stat.h>
43#include <sys/wait.h>
44#include <unistd.h>
45#include <stdlib.h>
46
47static const char* There_are_no_attachments = N_("There are no attachments.");
48
49#define CHECK_COUNT if (idxlen == 0) { mutt_error _(There_are_no_attachments); break; }
50
51
52
53enum
54{
55  HDR_FROM  = 1,
56  HDR_TO,
57  HDR_CC,
58  HDR_BCC,
59  HDR_SUBJECT,
60  HDR_REPLYTO,
61  HDR_FCC,
62
63#ifdef MIXMASTER
64  HDR_MIX,
65#endif
66
67  HDR_CRYPT,
68  HDR_CRYPTINFO,
69
70  HDR_ATTACH  = (HDR_FCC + 5) /* where to start printing the attachments */
71};
72
73#define HDR_XOFFSET 10
74#define TITLE_FMT "%10s" /* Used for Prompts, which are ASCII */
75#define W (COLS - HDR_XOFFSET)
76
77static char *Prompts[] =
78{
79  "From: ",
80  "To: ",
81  "Cc: ",
82  "Bcc: ",
83  "Subject: ",
84  "Reply-To: ",
85  "Fcc: "
86};
87
88static struct mapping_t ComposeHelp[] = {
89  { N_("Send"),    OP_COMPOSE_SEND_MESSAGE },
90  { N_("Abort"),   OP_EXIT },
91  { "To",      OP_COMPOSE_EDIT_TO },
92  { "CC",      OP_COMPOSE_EDIT_CC },
93  { "Subj",    OP_COMPOSE_EDIT_SUBJECT },
94  { N_("Attach file"),  OP_COMPOSE_ATTACH_FILE },
95  { N_("Descrip"), OP_COMPOSE_EDIT_DESCRIPTION },
96  { N_("Help"),    OP_HELP },
97  { NULL }
98};
99
100static void snd_entry (char *b, size_t blen, MUTTMENU *menu, int num)
101{
102    mutt_FormatString (b, blen, 0, NONULL (AttachFormat), mutt_attach_fmt,
103            (unsigned long)(((ATTACHPTR **) menu->data)[num]),
104            M_FORMAT_STAT_FILE | M_FORMAT_ARROWCURSOR);
105}
106
107
108
109#include "mutt_crypt.h"
110
111static void redraw_crypt_lines (HEADER *msg)
112{
113  int off = 0;
114
115  if ((WithCrypto & APPLICATION_PGP) && (WithCrypto & APPLICATION_SMIME))
116  {     
117    if (!msg->security)
118      mvaddstr (HDR_CRYPT, 0,     "Security: ");
119    else if (msg->security & APPLICATION_SMIME)
120      mvaddstr (HDR_CRYPT, 0,     "  S/MIME: ");
121    else if (msg->security & APPLICATION_PGP)
122      mvaddstr (HDR_CRYPT, 0,     "     PGP: ");
123  }
124  else if ((WithCrypto & APPLICATION_SMIME))
125    mvaddstr (HDR_CRYPT, 0,     "  S/MIME: ");
126  else if ((WithCrypto & APPLICATION_PGP))
127    mvaddstr (HDR_CRYPT, 0,     "     PGP: ");
128  else
129    return;
130
131  if ((msg->security & (ENCRYPT | SIGN)) == (ENCRYPT | SIGN))
132    addstr (_("Sign, Encrypt"));
133  else if (msg->security & ENCRYPT)
134    addstr (_("Encrypt"));
135  else if (msg->security & SIGN)
136    addstr (_("Sign"));
137  else
138    addstr (_("Clear"));
139
140  if ((WithCrypto & APPLICATION_PGP))
141    if ((msg->security & APPLICATION_PGP) 
142        && (msg->security & (ENCRYPT | SIGN)))
143    {
144      if ((msg->security & INLINE))
145        addstr (_(" (inline)"));
146      else
147        addstr (_(" (PGP/MIME)"));
148    }
149  clrtoeol ();
150
151  move (HDR_CRYPTINFO, 0);
152  clrtoeol ();
153  if ((WithCrypto & APPLICATION_PGP)
154      && msg->security & APPLICATION_PGP  && msg->security & SIGN)
155    printw ("%s%s", _(" sign as: "), PgpSignAs ? PgpSignAs : _("<default>"));
156
157  if ((WithCrypto & APPLICATION_SMIME)
158      && msg->security & APPLICATION_SMIME  && msg->security & SIGN) {
159      printw ("%s%s", _(" sign as: "), SmimeDefaultKey ? SmimeDefaultKey : _("<default>"));
160  }
161
162  if ((WithCrypto & APPLICATION_SMIME)
163      && (msg->security & APPLICATION_SMIME)
164      && (msg->security & ENCRYPT)
165      && SmimeCryptAlg
166      && *SmimeCryptAlg) {
167      mvprintw (HDR_CRYPTINFO, 40, "%s%s", _("Encrypt with: "),
168                NONULL(SmimeCryptAlg));
169      off = 20;
170  }
171}
172
173
174#ifdef MIXMASTER
175
176static void redraw_mix_line (LIST *chain)
177{
178  int c;
179  char *t;
180
181  mvaddstr (HDR_MIX, 0,     "     Mix: ");
182
183  if (!chain)
184  {
185    addstr ("<no chain defined>");
186    clrtoeol ();
187    return;
188  }
189 
190  for (c = 12; chain; chain = chain->next)
191  {
192    t = chain->data;
193    if (t && t[0] == '0' && t[1] == '\0')
194      t = "<random>";
195   
196    if (c + mutt_strlen (t) + 2 >= COLS)
197      break;
198
199    addstr (NONULL(t));
200    if (chain->next)
201      addstr (", ");
202
203    c += mutt_strlen (t) + 2;
204  }
205}
206#endif /* MIXMASTER */
207
208static int
209check_attachments(ATTACHPTR **idx, short idxlen)
210{
211  int i, r;
212  struct stat st;
213  char pretty[_POSIX_PATH_MAX], msg[_POSIX_PATH_MAX + SHORT_STRING];
214
215  for (i = 0; i < idxlen; i++)
216  {
217    strfcpy(pretty, idx[i]->content->filename, sizeof(pretty));
218    if(stat(idx[i]->content->filename, &st) != 0)
219    {
220      mutt_pretty_mailbox(pretty, sizeof (pretty));
221      mutt_error(_("%s [#%d] no longer exists!"),
222                 pretty, i+1);
223      return -1;
224    }
225   
226    if(idx[i]->content->stamp < st.st_mtime)
227    {
228      mutt_pretty_mailbox(pretty, sizeof (pretty));
229      snprintf(msg, sizeof(msg), _("%s [#%d] modified. Update encoding?"),
230               pretty, i+1);
231     
232      if((r = mutt_yesorno(msg, M_YES)) == M_YES)
233        mutt_update_encoding(idx[i]->content);
234      else if(r == -1)
235        return -1;
236    }
237  }
238
239  return 0;
240}
241
242static void draw_envelope_addr (int line, ADDRESS *addr)
243{
244  char buf[STRING];
245
246  buf[0] = 0;
247  rfc822_write_address (buf, sizeof (buf), addr, 1);
248  mvprintw (line, 0, TITLE_FMT, Prompts[line - 1]);
249  mutt_paddstr (W, buf);
250}
251
252static void draw_envelope (HEADER *msg, char *fcc)
253{
254  draw_envelope_addr (HDR_FROM, msg->env->from);
255  draw_envelope_addr (HDR_TO, msg->env->to);
256  draw_envelope_addr (HDR_CC, msg->env->cc);
257  draw_envelope_addr (HDR_BCC, msg->env->bcc);
258  mvprintw (HDR_SUBJECT, 0, TITLE_FMT, Prompts[HDR_SUBJECT - 1]);
259  mutt_paddstr (W, NONULL (msg->env->subject));
260  draw_envelope_addr (HDR_REPLYTO, msg->env->reply_to);
261  mvprintw (HDR_FCC, 0, TITLE_FMT, Prompts[HDR_FCC - 1]);
262  mutt_paddstr (W, fcc);
263
264  if (WithCrypto)
265    redraw_crypt_lines (msg);
266
267#ifdef MIXMASTER
268  redraw_mix_line (msg->chain);
269#endif
270
271  SETCOLOR (MT_COLOR_STATUS);
272  mvaddstr (HDR_ATTACH - 1, 0, _("-- Attachments"));
273  BKGDSET (MT_COLOR_STATUS);
274  clrtoeol ();
275
276  BKGDSET (MT_COLOR_NORMAL);
277  SETCOLOR (MT_COLOR_NORMAL);
278}
279
280static int edit_address_list (int line, ADDRESS **addr)
281{
282  char buf[HUGE_STRING] = ""; /* needs to be large for alias expansion */
283  char *err = NULL;
284 
285  mutt_addrlist_to_local (*addr);
286  rfc822_write_address (buf, sizeof (buf), *addr, 0);
287  if (mutt_get_field (Prompts[line - 1], buf, sizeof (buf), M_ALIAS) == 0)
288  {
289    rfc822_free_address (addr);
290    *addr = mutt_parse_adrlist (*addr, buf);
291    *addr = mutt_expand_aliases (*addr);
292  }
293
294  if (option (OPTNEEDREDRAW))
295  {
296    unset_option (OPTNEEDREDRAW);
297    return (REDRAW_FULL);
298  }
299
300  if (mutt_addrlist_to_idna (*addr, &err) != 0)
301  {
302    mutt_error (_("Warning: '%s' is a bad IDN."), err);
303    mutt_refresh();
304    FREE (&err);
305  }
306
307  /* redraw the expanded list so the user can see the result */
308  buf[0] = 0;
309  rfc822_write_address (buf, sizeof (buf), *addr, 1);
310  move (line, HDR_XOFFSET);
311  mutt_paddstr (W, buf);
312 
313  return 0;
314}
315
316static int delete_attachment (MUTTMENU *menu, short *idxlen, int x)
317{
318  ATTACHPTR **idx = (ATTACHPTR **) menu->data;
319  int y;
320
321  menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
322
323  if (x == 0 && menu->max == 1)
324  {
325    mutt_error _("You may not delete the only attachment.");
326    idx[x]->content->tagged = 0;
327    return (-1);
328  }
329
330  for (y = 0; y < *idxlen; y++)
331  {
332    if (idx[y]->content->next == idx[x]->content)
333    {
334      idx[y]->content->next = idx[x]->content->next;
335      break;
336    }
337  }
338
339  idx[x]->content->next = NULL;
340  idx[x]->content->parts = NULL;
341  mutt_free_body (&(idx[x]->content));
342  FREE (&idx[x]->tree);
343  FREE (&idx[x]);
344  for (; x < *idxlen - 1; x++)
345    idx[x] = idx[x+1];
346  menu->max = --(*idxlen);
347 
348  return (0);
349}
350
351static void update_idx (MUTTMENU *menu, ATTACHPTR **idx, short idxlen)
352{
353  idx[idxlen]->level = (idxlen > 0) ? idx[idxlen-1]->level : 0;
354  if (idxlen)
355    idx[idxlen - 1]->content->next = idx[idxlen]->content;
356  idx[idxlen]->content->aptr = idx[idxlen];
357  menu->current = idxlen++;
358  mutt_update_tree (idx, idxlen);
359  menu->max = idxlen;
360  return;
361}
362
363
364/*
365 * cum_attachs_size: Cumulative Attachments Size
366 *
367 * Returns the total number of bytes used by the attachments in the
368 * attachment list _after_ content-transfer-encodings have been
369 * applied.
370 *
371 */
372
373static unsigned long cum_attachs_size (MUTTMENU *menu)
374{
375  size_t s;
376  unsigned short i;
377  ATTACHPTR **idx = menu->data;
378  CONTENT *info;
379  BODY *b;
380 
381  for (i = 0, s = 0; i < menu->max; i++)
382  {
383    b = idx[i]->content;
384
385    if (!b->content)
386      b->content = mutt_get_content_info (b->filename, b);
387
388    if ((info = b->content))
389    {
390      switch (b->encoding)
391      {
392        case ENCQUOTEDPRINTABLE:
393          s += 3 * (info->lobin + info->hibin) + info->ascii + info->crlf;
394          break;
395        case ENCBASE64:
396          s += (4 * (info->lobin + info->hibin + info->ascii + info->crlf)) / 3;
397          break;
398        default:
399          s += info->lobin + info->hibin + info->ascii + info->crlf;
400          break;
401      }
402    }
403  }
404
405  return s;
406}
407
408/* prototype for use below */
409static void compose_status_line (char *buf, size_t buflen, size_t col, MUTTMENU *menu, 
410      const char *p);
411
412/*
413 * compose_format_str()
414 *
415 * %a = total number of attachments
416 * %h = hostname  [option]
417 * %l = approx. length of current message (in bytes)
418 * %v = Mutt version
419 *
420 * This function is similar to status_format_str().  Look at that function for
421 * help when modifying this function.
422 */
423
424static const char *
425compose_format_str (char *buf, size_t buflen, size_t col, char op, const char *src,
426                   const char *prefix, const char *ifstring,
427                   const char *elsestring,
428                   unsigned long data, format_flag flags)
429{
430  char fmt[SHORT_STRING], tmp[SHORT_STRING];
431  int optional = (flags & M_FORMAT_OPTIONAL);
432  MUTTMENU *menu = (MUTTMENU *) data;
433
434  *buf = 0;
435  switch (op)
436  {
437    case 'a': /* total number of attachments */
438        snprintf (fmt, sizeof (fmt), "%%%sd", prefix);
439        snprintf (buf, buflen, fmt, menu->max);
440      break;
441
442    case 'h'/* hostname */
443      snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
444      snprintf (buf, buflen, fmt, NONULL(Hostname));
445      break;
446
447    case 'l': /* approx length of current message in bytes */
448        snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
449        mutt_pretty_size (tmp, sizeof (tmp), menu ? cum_attachs_size(menu) : 0);
450        snprintf (buf, buflen, fmt, tmp);
451      break;
452
453    case 'v':
454      snprintf (fmt, sizeof (fmt), "Mutt %%s");
455      snprintf (buf, buflen, fmt, MUTT_VERSION);
456      break;
457
458    case 0:
459      *buf = 0;
460      return (src);
461
462    default:
463      snprintf (buf, buflen, "%%%s%c", prefix, op);
464      break;
465  }
466
467  if (optional)
468    compose_status_line (buf, buflen, col, menu, ifstring);
469  else if (flags & M_FORMAT_OPTIONAL)
470    compose_status_line (buf, buflen, col, menu, elsestring);
471
472  return (src);
473}
474
475static void compose_status_line (char *buf, size_t buflen, size_t col, MUTTMENU *menu, 
476      const char *p)
477{
478  mutt_FormatString (buf, buflen, col, p, compose_format_str, 
479        (unsigned long) menu, 0);
480}
481
482
483/* return values:
484 *
485 * 1    message should be postponed
486 * 0    normal exit
487 * -1   abort message
488 */
489int mutt_compose_menu (HEADER *msg,   /* structure for new message */
490                    char *fcc,     /* where to save a copy of the message */
491                    size_t fcclen,
492                    HEADER *cur)   /* current message */
493{
494  char helpstr[LONG_STRING];
495  char buf[LONG_STRING];
496  char fname[_POSIX_PATH_MAX];
497  MUTTMENU *menu;
498  ATTACHPTR **idx = NULL;
499  short idxlen = 0;
500  short idxmax = 0;
501  int i, close = 0;
502  int r = -1;           /* return value */
503  int op = 0;
504  int loop = 1;
505  int fccSet = 0;       /* has the user edited the Fcc: field ? */
506  CONTEXT *ctx = NULL, *this = NULL;
507  /* Sort, SortAux could be changed in mutt_index_menu() */
508  int oldSort, oldSortAux;
509  struct stat st;
510
511  mutt_attach_init (msg->content);
512  idx = mutt_gen_attach_list (msg->content, -1, idx, &idxlen, &idxmax, 0, 1);
513
514  menu = mutt_new_menu ();
515  menu->menu = MENU_COMPOSE;
516  menu->offset = HDR_ATTACH;
517  menu->max = idxlen;
518  menu->make_entry = snd_entry;
519  menu->tag = mutt_tag_attach;
520  menu->data = idx;
521  menu->help = mutt_compile_help (helpstr, sizeof (helpstr), MENU_COMPOSE, ComposeHelp);
522 
523  while (loop)
524  {
525    switch (op = mutt_menuLoop (menu))
526    {
527      case OP_REDRAW:
528        draw_envelope (msg, fcc);
529        menu->offset = HDR_ATTACH;
530        menu->pagelen = LINES - HDR_ATTACH - 2;
531        break;
532      case OP_COMPOSE_EDIT_FROM:
533        menu->redraw = edit_address_list (HDR_FROM, &msg->env->from);
534        mutt_message_hook (NULL, msg, M_SEND2HOOK);
535        break;
536      case OP_COMPOSE_EDIT_TO:
537        menu->redraw = edit_address_list (HDR_TO, &msg->env->to);
538        mutt_message_hook (NULL, msg, M_SEND2HOOK);
539        break;
540      case OP_COMPOSE_EDIT_BCC:
541        menu->redraw = edit_address_list (HDR_BCC, &msg->env->bcc);
542        mutt_message_hook (NULL, msg, M_SEND2HOOK);
543        break;
544      case OP_COMPOSE_EDIT_CC:
545        menu->redraw = edit_address_list (HDR_CC, &msg->env->cc);
546        mutt_message_hook (NULL, msg, M_SEND2HOOK);     
547        break;
548      case OP_COMPOSE_EDIT_SUBJECT:
549        if (msg->env->subject)
550          strfcpy (buf, msg->env->subject, sizeof (buf));
551        else
552          buf[0] = 0;
553        if (mutt_get_field ("Subject: ", buf, sizeof (buf), 0) == 0)
554        {
555          mutt_str_replace (&msg->env->subject, buf);
556          move (HDR_SUBJECT, HDR_XOFFSET);
557          clrtoeol ();
558          if (msg->env->subject)
559            mutt_paddstr (W, msg->env->subject);
560        }
561        mutt_message_hook (NULL, msg, M_SEND2HOOK);
562        break;
563      case OP_COMPOSE_EDIT_REPLY_TO:
564        menu->redraw = edit_address_list (HDR_REPLYTO, &msg->env->reply_to);
565        mutt_message_hook (NULL, msg, M_SEND2HOOK);
566        break;
567      case OP_COMPOSE_EDIT_FCC:
568        strfcpy (buf, fcc, sizeof (buf));
569        if (mutt_get_field ("Fcc: ", buf, sizeof (buf), M_FILE | M_CLEAR) == 0)
570        {
571          strfcpy (fcc, buf, fcclen);
572          mutt_pretty_mailbox (fcc, fcclen);
573          move (HDR_FCC, HDR_XOFFSET);
574          mutt_paddstr (W, fcc);
575          fccSet = 1;
576        }
577        MAYBE_REDRAW (menu->redraw);
578        mutt_message_hook (NULL, msg, M_SEND2HOOK);
579        break;
580      case OP_COMPOSE_EDIT_MESSAGE:
581        if (Editor && (mutt_strcmp ("builtin", Editor) != 0) && !option (OPTEDITHDRS))
582        {
583          mutt_edit_file (Editor, msg->content->filename);
584          mutt_update_encoding (msg->content);
585          menu->redraw = REDRAW_CURRENT | REDRAW_STATUS;
586          mutt_message_hook (NULL, msg, M_SEND2HOOK);
587          break;
588        }
589        /* fall through */
590      case OP_COMPOSE_EDIT_HEADERS:
591        if (mutt_strcmp ("builtin", Editor) != 0 &&
592            (op == OP_COMPOSE_EDIT_HEADERS ||
593            (op == OP_COMPOSE_EDIT_MESSAGE && option (OPTEDITHDRS))))
594        {
595          char *tag = NULL, *err = NULL;
596          mutt_env_to_local (msg->env);
597          mutt_edit_headers (NONULL (Editor), msg->content->filename, msg,
598                             fcc, fcclen);
599          if (mutt_env_to_idna (msg->env, &tag, &err))
600          {
601            mutt_error (_("Bad IDN in \"%s\": '%s'"), tag, err);
602            FREE (&err);
603          }
604        }
605        else
606        {
607          /* this is grouped with OP_COMPOSE_EDIT_HEADERS because the
608             attachment list could change if the user invokes ~v to edit
609             the message with headers, in which we need to execute the
610             code below to regenerate the index array */
611          mutt_builtin_editor (msg->content->filename, msg, cur);
612        }
613        mutt_update_encoding (msg->content);
614
615        /* attachments may have been added */
616        if (idxlen && idx[idxlen - 1]->content->next)
617        {
618          for (i = 0; i < idxlen; i++)
619            FREE (&idx[i]);
620          idxlen = 0;
621          idx = mutt_gen_attach_list (msg->content, -1, idx, &idxlen, &idxmax, 0, 1);
622          menu->data = idx;
623          menu->max = idxlen;
624        }
625
626        menu->redraw = REDRAW_FULL;
627        mutt_message_hook (NULL, msg, M_SEND2HOOK);
628        break;
629
630
631
632      case OP_COMPOSE_ATTACH_KEY:
633        if (!(WithCrypto & APPLICATION_PGP))
634          break;       
635        if (idxlen == idxmax)
636        {
637          safe_realloc (&idx, sizeof (ATTACHPTR *) * (idxmax += 5));
638          menu->data = idx;
639        }
640       
641        idx[idxlen] = (ATTACHPTR *) safe_calloc (1, sizeof (ATTACHPTR));
642        if ((idx[idxlen]->content = crypt_pgp_make_key_attachment(NULL)) != NULL)
643        {
644          update_idx (menu, idx, idxlen++);
645          menu->redraw |= REDRAW_INDEX;
646        }
647        else
648          FREE (&idx[idxlen]);
649
650        menu->redraw |= REDRAW_STATUS;
651
652        if (option(OPTNEEDREDRAW))
653        {
654          menu->redraw = REDRAW_FULL;
655          unset_option(OPTNEEDREDRAW);
656        }
657       
658        mutt_message_hook (NULL, msg, M_SEND2HOOK);
659        break;
660
661
662      case OP_COMPOSE_ATTACH_FILE:
663        {
664          char *prompt, **files;
665          int error, numfiles;
666
667          fname[0] = 0;
668          prompt = _("Attach file");
669          numfiles = 0;
670          files = NULL;
671
672          if (_mutt_enter_fname (prompt, fname, sizeof (fname), &menu->redraw, 0, 1, &files, &numfiles) == -1 ||
673              *fname == '\0')
674            break;
675
676          if (idxlen + numfiles >= idxmax)
677          {
678            safe_realloc (&idx, sizeof (ATTACHPTR *) * (idxmax += 5 + numfiles));
679            menu->data = idx;
680          }
681
682          error = 0;
683          if (numfiles > 1)
684            mutt_message _("Attaching selected files...");
685          for (i = 0; i < numfiles; i++)
686          {
687            char *att = files[i