diff options
author | Eduardo Chappa <chappa@washington.edu> | 2014-12-07 19:42:37 -0700 |
---|---|---|
committer | Eduardo Chappa <chappa@washington.edu> | 2014-12-07 19:42:37 -0700 |
commit | 20d433c77e32dc05c2accf8ab943c2a7d9738239 (patch) | |
tree | 4cf7be2b55ec529067a2867eb29ebcfe6c5758aa /pith | |
parent | 121a42f3d82c1b98c384857960d14b2057a95c41 (diff) | |
download | alpine-20d433c77e32dc05c2accf8ab943c2a7d9738239.tar.xz |
* new version 2.19.9993
* Aggregate operations allows bouncing a list of messages using a role.
Suggested by Ulf-Dietrich Braumann.
* Compilation error of module pith/reply.c if SMIME is not defined (as in
Windows Alpine). There was a misplaced parenthesis.
* Update to S/MIME to explain how to use a PKCS12 certificate in
Alpine.
* Fix error in compare_certs function, that would modify the name of
the certificates after sorting them, and return when no certificates
are given.
* When replying to several messages, subject will be decoded first,
and then stripped from re/fwd before they are compared to determine
the subject of the replied message.
* Add $(LIBINTL) to the flags to link rpdump, rpload, alpined and
alpineldap because MAC OSX 10.8 x86_64 needs it.
* When the download of an attachment is interrumpted, Alpine stills
caches what was downloaded, making the download incomplete for
subsequent calls of Alpine attempting to open the attachment. In the
future, Alpine will not cache any downloaded part of the attachment
when it is interrupted.
Diffstat (limited to 'pith')
-rw-r--r-- | pith/adrbklib.c | 8 | ||||
-rw-r--r-- | pith/charconv/filesys.c | 2 | ||||
-rw-r--r-- | pith/charset.c | 6 | ||||
-rw-r--r-- | pith/conf.c | 8 | ||||
-rw-r--r-- | pith/conftype.h | 10 | ||||
-rw-r--r-- | pith/detach.c | 12 | ||||
-rw-r--r-- | pith/filter.c | 8 | ||||
-rw-r--r-- | pith/folder.c | 2 | ||||
-rw-r--r-- | pith/imap.c | 2 | ||||
-rw-r--r-- | pith/ldap.c | 2 | ||||
-rw-r--r-- | pith/mailcap.c | 2 | ||||
-rw-r--r-- | pith/mailcmd.c | 2 | ||||
-rw-r--r-- | pith/mailindx.c | 6 | ||||
-rw-r--r-- | pith/mimetype.c | 2 | ||||
-rw-r--r-- | pith/pattern.c | 8 | ||||
-rw-r--r-- | pith/pine.hlp | 71 | ||||
-rw-r--r-- | pith/reply.c | 37 | ||||
-rw-r--r-- | pith/reply.h | 1 | ||||
-rw-r--r-- | pith/save.c | 2 | ||||
-rw-r--r-- | pith/send.c | 8 | ||||
-rw-r--r-- | pith/smime.c | 27 | ||||
-rw-r--r-- | pith/smkeys.c | 270 | ||||
-rw-r--r-- | pith/smkeys.h | 8 | ||||
-rw-r--r-- | pith/sort.c | 2 | ||||
-rw-r--r-- | pith/stream.c | 2 | ||||
-rw-r--r-- | pith/string.c | 2 | ||||
-rw-r--r-- | pith/util.h | 2 |
27 files changed, 409 insertions, 103 deletions
diff --git a/pith/adrbklib.c b/pith/adrbklib.c index bdd505fb..3b0f17e8 100644 --- a/pith/adrbklib.c +++ b/pith/adrbklib.c @@ -4343,7 +4343,7 @@ exp_set_expanded(EXPANDED_S *exp_head, a_c_arg_t n) nn = (adrbk_cntr_t)n; if(!exp_head) - panic("exp_head not set in exp_set_expanded"); + alpine_panic("exp_head not set in exp_set_expanded"); for(e = exp_head; e->next; e = e->next) if(e->next->ent >= nn) @@ -4375,7 +4375,7 @@ exp_unset_expanded(EXPANDED_S *exp_head, a_c_arg_t n) nn = (adrbk_cntr_t)n; if(!exp_head) - panic("exp_head not set in exp_unset_expanded"); + alpine_panic("exp_head not set in exp_unset_expanded"); for(e = exp_head; e->next; e = e->next) if(e->next->ent >= nn) @@ -4407,7 +4407,7 @@ exp_del_nth(EXPANDED_S *exp_head, a_c_arg_t n) nn = (adrbk_cntr_t)n; if(!exp_head) - panic("exp_head not set in exp_del_nth"); + alpine_panic("exp_head not set in exp_del_nth"); e = exp_head->next; while(e && e->ent < nn) @@ -4447,7 +4447,7 @@ exp_add_nth(EXPANDED_S *exp_head, a_c_arg_t n) nn = (adrbk_cntr_t)n; if(!exp_head) - panic("exp_head not set in exp_add_nth"); + alpine_panic("exp_head not set in exp_add_nth"); e = exp_head->next; while(e && e->ent < nn) diff --git a/pith/charconv/filesys.c b/pith/charconv/filesys.c index 63ac7b13..95da1e4b 100644 --- a/pith/charconv/filesys.c +++ b/pith/charconv/filesys.c @@ -4,8 +4,8 @@ static char rcsid[] = "$Id: filesys.c 770 2007-10-24 00:23:09Z hubert@u.washingt /* * ======================================================================== - * Copyright 2006-2007 University of Washington * Copyright 2013-2014 Eduardo Chappa + * Copyright 2006-2007 University of Washington * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/pith/charset.c b/pith/charset.c index 6c16d624..db41adf6 100644 --- a/pith/charset.c +++ b/pith/charset.c @@ -424,7 +424,7 @@ rfc1522_copy_and_transliterate(unsigned char *rv, if(utf8_charset(cset)){ if(!utf8_text(&src, cset, &xsrc, 0L)){ /* should not happen */ - panic("c-client failed to transliterate recognized characterset"); + alpine_panic("c-client failed to transliterate recognized characterset"); } } else{ @@ -451,7 +451,7 @@ rfc1522_copy_and_transliterate(unsigned char *rv, if(!(cs->type == CT_ASCII || cs->type == CT_UTF8)){ if(!utf8_text_cs(&src, cs, &xsrc, 0L, 0L)){ /* should not happen */ - panic("c-client failed to transliterate recognized characterset"); + alpine_panic("c-client failed to transliterate recognized characterset"); } } } @@ -461,7 +461,7 @@ rfc1522_copy_and_transliterate(unsigned char *rv, && utf8_charset(cset)){ if(!utf8_text(&src, cset, &xsrc, 0L)){ /* should not happen */ - panic("c-client failed to transliterate recognized character set"); + alpine_panic("c-client failed to transliterate recognized character set"); } } else{ diff --git a/pith/conf.c b/pith/conf.c index 81eed75b..d6083f01 100644 --- a/pith/conf.c +++ b/pith/conf.c @@ -1845,7 +1845,7 @@ init_vars(struct pine *ps, void (*cmds_f) (struct pine *, char **)) */ if(reset_character_set_stuff(&err) == -1) - panic(err ? err : "trouble with character set setup"); + alpine_panic(err ? err : "trouble with character set setup"); else if(err){ init_error(ps, SM_ORDER | SM_DING, 3, 5, err); fs_give((void **) &err); @@ -2646,7 +2646,7 @@ convert_configvar_to_utf8(struct variable *v, char *fromcharset) case 4: valptr = v->global_val.l; break; case 5: valptr = v->fixed_val.l; break; case 6: valptr = v->cmdline_val.l; break; - default: panic("bad case in convert_configvar"); + default: alpine_panic("bad case in convert_configvar"); } if(valptr){ @@ -2672,7 +2672,7 @@ convert_configvar_to_utf8(struct variable *v, char *fromcharset) case 4: valptr = &v->global_val.p; break; case 5: valptr = &v->fixed_val.p; break; case 6: valptr = &v->cmdline_val.p; break; - default: panic("bad case in convert_configvar"); + default: alpine_panic("bad case in convert_configvar"); } if(valptr && *valptr && (*valptr)[0]){ @@ -7606,7 +7606,7 @@ panic1(char *message, char *arg) snprintf(buf1, sizeof(buf1), "%.*s", MAX(sizeof(buf1) - 1 - strlen(message), 0), arg); snprintf(buf2, sizeof(buf2), message, buf1); - panic(buf2); + alpine_panic(buf2); } diff --git a/pith/conftype.h b/pith/conftype.h index 11bf778f..d1931609 100644 --- a/pith/conftype.h +++ b/pith/conftype.h @@ -709,20 +709,28 @@ typedef struct smime_stuff { char *publicpath; char *publiccontent; CertList *publiccertlist; + CertList *backuppubliccertlist; SmimeHolderType privatetype; char *privatepath; char *privatecontent; CertList *privatecertlist; - void *personal_certs; /* this is type (PERSONAL_CERT *) */ + CertList *backupprivatecertlist; + void *backuppersonal_certs; /* this is type (PERSONAL_CERT *) */ + void *personal_certs; /* this is type (PERSONAL_CERT *) */ SmimeHolderType catype; char *capath; char *cacontent; CertList *cacertlist; + CertList *backupcacertlist; } SMIME_STUFF_S; +#define BACKUPDATACERT(X) (((X) == Public ? ps_global->smime->backuppubliccertlist \ + : ((X) == Private ? ps_global->smime->backupprivatecertlist \ + : ps_global->smime->backupcacertlist))) + #define DATACERT(X) (((X) == Public ? ps_global->smime->publiccertlist \ : ((X) == Private ? ps_global->smime->privatecertlist \ : ps_global->smime->cacertlist))) diff --git a/pith/detach.c b/pith/detach.c index aebadae6..5c4b08e7 100644 --- a/pith/detach.c +++ b/pith/detach.c @@ -83,7 +83,7 @@ int df_trigger_cmp(long, char *, LT_INS_S **, void *); int df_trigger_cmp_text(char *, char *); int df_trigger_cmp_lwsp(char *, char *); int df_trigger_cmp_start(char *, char *); -int fetch_readc_cleanup(void); +int fetch_readc_cleanup(int); char *fetch_gets(readfn_t, void *, unsigned long, GETS_DATA *); int fetch_readc(unsigned char *); @@ -644,7 +644,7 @@ fetch_readc_init(FETCH_READC_S *frd, MAILSTREAM *stream, long int msgno, int -fetch_readc_cleanup(void) +fetch_readc_cleanup(int store) { if(g_fr_desc){ if(g_fr_desc->we_turned_on) @@ -653,7 +653,7 @@ fetch_readc_cleanup(void) if(g_fr_desc->chunk && g_fr_desc->free_me) fs_give((void **) &g_fr_desc->chunk); - if(g_fr_desc->cache){ + if(g_fr_desc->cache && store){ SIZEDTEXT text; text.size = g_fr_desc->size; @@ -695,7 +695,7 @@ fetch_readc(unsigned char *c) extern void gf_error(char *); if(ps_global->intr_pending){ - (void) fetch_readc_cleanup(); + (void) fetch_readc_cleanup(0); /* TRANSLATORS: data transfer was interrupted by something */ gf_error(g_fr_desc->error ? g_fr_desc->error :_("Transfer interrupted!")); /* no return */ @@ -822,13 +822,13 @@ fetch_readc(unsigned char *c) mail_parameters(g_fr_desc->stream, SET_GETS, old_gets); if(!rv){ - (void) fetch_readc_cleanup(); + (void) fetch_readc_cleanup(0); gf_error("Partial fetch failed!"); /* no return */ } } else /* clean up and return done. */ - return(fetch_readc_cleanup()); + return(fetch_readc_cleanup(1)); } *c = *g_fr_desc->chunkp++; diff --git a/pith/filter.c b/pith/filter.c index d1848f87..5fd63c71 100644 --- a/pith/filter.c +++ b/pith/filter.c @@ -364,10 +364,10 @@ gf_clear_so_readc(STORE_S *so) fs_give((void **) &sp); } else - panic("Programmer botch: Can't unstack store readc"); + alpine_panic("Programmer botch: Can't unstack store readc"); } else - panic("Programmer botch: NULL store clearing store readc"); + alpine_panic("Programmer botch: NULL store clearing store readc"); } @@ -398,10 +398,10 @@ gf_clear_so_writec(STORE_S *so) fs_give((void **) &sp); } else - panic("Programmer botch: Can't unstack store writec"); + alpine_panic("Programmer botch: Can't unstack store writec"); } else - panic("Programmer botch: NULL store clearing store writec"); + alpine_panic("Programmer botch: NULL store clearing store writec"); } diff --git a/pith/folder.c b/pith/folder.c index da0ea6d5..afc5f070 100644 --- a/pith/folder.c +++ b/pith/folder.c @@ -592,7 +592,7 @@ init_folders(struct pine *ps) * collection??? */ if(!prime) - panic(_("No folder collections defined")); + alpine_panic(_("No folder collections defined")); /* * At this point, insert the INBOX mapping as the leading diff --git a/pith/imap.c b/pith/imap.c index 90219992..4b81bc09 100644 --- a/pith/imap.c +++ b/pith/imap.c @@ -643,7 +643,7 @@ mm_nocritical(MAILSTREAM *stream) void mm_fatal(char *message) { - panic(message); + alpine_panic(message); } diff --git a/pith/ldap.c b/pith/ldap.c index 6fc96303..e3d0fa59 100644 --- a/pith/ldap.c +++ b/pith/ldap.c @@ -1700,7 +1700,7 @@ our_ldap_set_option(LDAP *ld, int option, void *optdata) break; default: - panic("LDAP function not implemented"); + alpine_panic("LDAP function not implemented"); } #endif diff --git a/pith/mailcap.c b/pith/mailcap.c index 38dd6b68..77e1412d 100644 --- a/pith/mailcap.c +++ b/pith/mailcap.c @@ -248,7 +248,7 @@ mc_process_file(char *file) return; default: - panic("Programmer botch in mc_process_file"); + alpine_panic("Programmer botch in mc_process_file"); /*NOTREACHED*/ } diff --git a/pith/mailcmd.c b/pith/mailcmd.c index 3f310e22..44a03fb8 100644 --- a/pith/mailcmd.c +++ b/pith/mailcmd.c @@ -1250,7 +1250,7 @@ first_recent: break; default: - panic("Unexpected incoming startup case"); + alpine_panic("Unexpected incoming startup case"); break; } diff --git a/pith/mailindx.c b/pith/mailindx.c index d420250a..afc241e1 100644 --- a/pith/mailindx.c +++ b/pith/mailindx.c @@ -1210,7 +1210,7 @@ setup_index_header_widths(MAILSTREAM *stream) break; default: - panic("Unhandled fixed case in setup_index_header"); + alpine_panic("Unhandled fixed case in setup_index_header"); break; } } @@ -3319,7 +3319,7 @@ simple_index_line(char *buf, size_t buflen, ICE_S *ice, long int msgno) IELEM_S *ielem; if(!buf) - panic("NULL buf in simple_index_line()"); + alpine_panic("NULL buf in simple_index_line()"); if(buflen > 0) buf[0] = '\0'; @@ -5093,7 +5093,7 @@ prio_str(INDEXDATA_S *idata, IndexColType ctype, ICE_S *ice) break; default: - panic("Unhandled case in prio_str"); + alpine_panic("Unhandled case in prio_str"); break; } diff --git a/pith/mimetype.c b/pith/mimetype.c index 0d6f0e26..cd6cf751 100644 --- a/pith/mimetype.c +++ b/pith/mimetype.c @@ -176,7 +176,7 @@ mt_srch_mime_type(MT_OPERATORPROC mt_operator, MT_MAP_T *mt_map) } } else - panic("Unhandled mime type search"); + alpine_panic("Unhandled mime type search"); } /* if we still can not find the type, but it is a .docx (or alike) extension diff --git a/pith/pattern.c b/pith/pattern.c index 0b2eaf58..757d0b2f 100644 --- a/pith/pattern.c +++ b/pith/pattern.c @@ -1295,9 +1295,9 @@ parse_patgrp_slash(char *str, PATGRP_S *patgrp) char *p; if(!patgrp) - panic("NULL patgrp to parse_patgrp_slash"); + alpine_panic("NULL patgrp to parse_patgrp_slash"); else if(!(str && *str)){ - panic("NULL or empty string to parse_patgrp_slash"); + alpine_panic("NULL or empty string to parse_patgrp_slash"); patgrp->bogus = 1; } else if(!strncmp(str, "/NICK=", 6)) @@ -1516,9 +1516,9 @@ parse_action_slash(char *str, ACTION_S *action) NAMEVAL_S *v; if(!action) - panic("NULL action to parse_action_slash"); + alpine_panic("NULL action to parse_action_slash"); else if(!(str && *str)) - panic("NULL or empty string to parse_action_slash"); + alpine_panic("NULL or empty string to parse_action_slash"); else if(!strncmp(str, "/ROLE=1", 7)) action->is_a_role = 1; else if(!strncmp(str, "/OTHER=1", 8)) diff --git a/pith/pine.hlp b/pith/pine.hlp index cb0a6d63..c22dbd84 100644 --- a/pith/pine.hlp +++ b/pith/pine.hlp @@ -140,7 +140,7 @@ with help text for the config screen and the composer that didn't have any reasonable place to be called from. Dummy change to get revision in pine.hlp ============= h_revision ================= -Alpine Commit 65 2014-06-20 23:23:15 +Alpine Commit 66 2014-12-07 19:42:29 ============= h_news ================= <HTML> <HEAD> @@ -257,8 +257,14 @@ Additions include: by James Jerkins. <LI> Replace tabs by spaces in From and Subject fields to control for size in screen of these fields. Change only in index screen display. + <LI> Aggregate operations allows bouncing a list of messages using a role. + Suggested by Ulf-Dietrich Braumann. + <LI> Makefile: Add $(LIBINTL) to the flags to link rpdump, rpload, + alpined and alpineldap because MAC OSX 10.8 x86_64 needs it. Reported by + Charles M. Register. </UL> + <P> Bugs that have been addressed include: <UL> @@ -294,6 +300,9 @@ Bugs that have been addressed include: that contains 8bit if the option "Enable 8bit ESMTP Negotiation" is enabled, the message contains 8bit characters and the smtp server supports 8bit sending. + <LI> When replying to several messages, subject will be decoded first, + and then stripped from re/fwd before they are compared to determine + the subject of the replied message. <LI> Crash when tcp connection to NNTP server was lost after connection had been established, but lost immediately afterwards. <LI> Crash with message "lock when already locked", when painting @@ -333,6 +342,11 @@ Bugs that have been addressed include: <LI> Move SSL configurations from UW-IMAP to configure script, and update OpenSSL configuration for Mac OS X. <LI> Remove -lregex from linker flags when building --with-supplied-regex. + <LI> When the download of an attachment is interrumpted, Alpine stills + caches what was downloaded, making the download incomplete for + subsequent calls of Alpine attempting to open the attachment. In the + future, Alpine will not cache any downloaded part of the attachment + when it is interrupted. </UL> <P> @@ -18714,7 +18728,7 @@ It has the format MMM DD. For example, "Oct 23". The feature <A HREF="h_config_dates_to_local"><!--#echo var="FEAT_convert-dates-to-localtime"--></A>, which adjusts for the timezone the message was sent from, -may have an affect on the value of this token as well as the values of +may have an effect on the value of this token as well as the values of all of the other DATE or TIME tokens. Some of the DATE and TIME tokens are displayed in a locale-specific way unless the option @@ -34310,18 +34324,41 @@ which gives you some information about the certificate used to sign or encrypt t <H2>MISCELLANEOUS</H2> -You may have access to a private certificate in the PKCS12 format, -which would sometimes be in a file with a ".p12" suffix. -The UNIX shell command +If you have access to a private certificate in the PKCS12 format, which +would sometimes be in a file with a ".p12" extension, then you can +use the following commands to generate private keys, public and certificate +authorities certificates. In the examples below, we assume that the +certificate in the p12 format is called "certificate.p12", and +that your email address is "your@address.com". + +<P> +In order to create a private key use the command +<P> +<CENTER><SAMP>openssl pkcs12 -in certificate.p12 -out your@address.com.key</SAMP></CENTER> <P> -<CENTER><SAMP>openssl pkcs12 -in file.p12 -out file.pem</SAMP></CENTER> +In order to create a public certificate use the command <P> -may work to convert that from the PKCS12 format to the PEM format. -Then that file could be placed in the "<SAMP>private</SAMP>" -directory with a filename of your email address followed by the -suffix "<SAMP>.key</SAMP>". +<CENTER><SAMP> +openssl pkcs12 -in certificate.p12 -clcerts -nokeys -out your@address.com.crt +</SAMP></CENTER> +<P> +In order to create a certificate authority certificate use the command +<P> +<CENTER><SAMP> +openssl pkcs12 -in certificate.p12 -cacerts -nokeys -out certificate-ca.crt +</SAMP></CENTER> + +<P> If the previous command produces an empty file, it means that the +certificate authority was not included in the .p12 file, so you will have +to get it from some other sources. You will need these certificates, so +that you can validate correctly signatures. <P> +After you have exported these certificates and keys, you can use the import +command in Alpine, from the S/MIME configuration screen, +to import these certificates into Alpine. They will be available for use +as soon as you import them. +<P> <End of help on this topic> </BODY> </HTML> @@ -35168,11 +35205,15 @@ The format of this screen is as follows. There are five fields: The leftmost field is normally empty, but it could contain the letter "D" to indicate that that certificate has been marked for deletion. The next field is the e-mail address of the owner of the -certificate, shown in its entirety. The third field is the first day of -validity for that certificate; the fourth field in the last day that that -certificate is valid, and the fifth field is what can be displayed of the -MD5 hash of the certificate. You can use any of the last three fields to -distinguish between two certificates for the same owner. +certificate, shown in its entirety. The third and fourth field are the +first and last day validity for that certificate, respectively. The date +is displayed in the user's locale unless the option +<A HREF="h_config_disable_index_locale_dates"><!--#echo var="FEAT_disable-index-locale-dates"--></A> +is set. In this case, the month, day and year are represented by two +digits, and the format used is mm/dd/yy. Finally, the fifth +field is what can be displayed of the MD5 hash of the certificate. You can +use any of the last three fields to distinguish between two certificates +for the same owner. <P> Available commands in this screen and a short description of what they do follows. diff --git a/pith/reply.c b/pith/reply.c index ec8293db..0b3aaa62 100644 --- a/pith/reply.c +++ b/pith/reply.c @@ -67,6 +67,39 @@ int (*pith_opt_reply_to_all_prompt)(int *); char *(*pith_opt_user_agent_prefix)(void); +/* compare two subjects and return if they are the same. + We compare stripped subjects, that is, those that do + not have re/fwd. Before we compare the subjects, we + decode them in case they were encoded. + Return value: 0 - not the same subject, 1 - same subject. +*/ + +int +same_subject(char *s, char *t) +{ + int rv = 0; + int i, j, len; + char *s1, *s2; /* holds decoded subjects from s and t */ + char *u, *v; + + if (s == NULL || t == NULL) + return s == t ? 1 : 0; + + i = strlen(s); j = strlen(t); + len = i < j ? j : i; + u = fs_get(6*len+1); + v = fs_get(6*len+1); + s1 = (char *) rfc1522_decode_to_utf8(u, 6*len + 1, s); + s2 = (char *) rfc1522_decode_to_utf8(v, 6*len + 1, t); + mail_strip_subject(s1, &u); + mail_strip_subject(s2, &v); + + rv = !strucmp(u, v); + + fs_give((void **) &u); + fs_give((void **) &v); + return rv; +} /* * reply_harvest - @@ -2259,9 +2292,9 @@ forward_body(MAILSTREAM *stream, ENVELOPE *env, struct mail_bodystruct *orig_bod && orig_body->nested.part->body.subtype && (!strucmp(orig_body->nested.part->body.subtype, OUR_PKCS7_ENCLOSURE_SUBTYPE) || !strucmp(orig_body->nested.part->body.subtype, "signed"))) - || !strucmp(orig_body->subtype, OUR_PKCS7_ENCLOSURE_SUBTYPE)) + || !strucmp(orig_body->subtype, OUR_PKCS7_ENCLOSURE_SUBTYPE) #endif /* SMIME */ - ){ + )){ /* only operate on the signed data (not the signature) */ body = forward_body(stream, env, &orig_body->nested.part->body, msgno, diff --git a/pith/reply.h b/pith/reply.h index 5a5ea516..ae6dbfed 100644 --- a/pith/reply.h +++ b/pith/reply.h @@ -101,6 +101,7 @@ int sigdashes_are_present(char *); char *signature_path(char *, char *, size_t); char *simple_read_remote_file(char *, char *); BODY *forward_multi_alt(MAILSTREAM *, ENVELOPE *, BODY *, long, char *, void *, gf_io_t, int); +int same_subject(char *, char *s); #endif /* PITH_REPLY_INCLUDED */ diff --git a/pith/save.c b/pith/save.c index c25d81f0..117dcc32 100644 --- a/pith/save.c +++ b/pith/save.c @@ -240,7 +240,7 @@ save_get_fldr_from_env(char *fbuf, int nfbuf, ENVELOPE *e, struct pine *state, break; default: - panic(botch); + alpine_panic(botch); break; } diff --git a/pith/send.c b/pith/send.c index e829d19c..d98f5c65 100644 --- a/pith/send.c +++ b/pith/send.c @@ -429,7 +429,7 @@ redraft_work(MAILSTREAM **streamp, long int cont_msg, ENVELOPE **outgoing, fields[++i] = "X-Our-ReplyTo"; /* ReplyTo is real */ fields[++i] = "Lcc"; /* Lcc: too... */ if(++i != FIELD_COUNT) - panic("Fix FIELD_COUNT"); + alpine_panic("Fix FIELD_COUNT"); for(pf = *custom; pf && pf->name; pf = pf->next) if(!pf->standard) @@ -1689,7 +1689,7 @@ ADDRESS * phone_home_from(void) { ADDRESS *addr = mail_newaddr(); - char tmp[32]; + char tmp[32]; /* garble up mailbox name */ snprintf(tmp, sizeof(tmp), "hash_%08u", phone_home_hash(ps_global->VAR_USER_ID)); @@ -1718,7 +1718,7 @@ phone_home_hash(char *s) h = ((h+1) * ((unsigned char) *s)) & (PH_MAXHASH - 1); } - + return (h); } @@ -3108,7 +3108,7 @@ encode_header_value(char *d, size_t dlen, unsigned char *s, char *charset, int e return((char *)s); if(dlen < SIZEOF_20KBUF) - panic("bad call to encode_header_value"); + alpine_panic("bad call to encode_header_value"); if(!encode_all){ /* diff --git a/pith/smime.c b/pith/smime.c index f29e37df..46e4278b 100644 --- a/pith/smime.c +++ b/pith/smime.c @@ -473,7 +473,7 @@ smime_expunge_cert(WhichCerts ctype) removed = 0; } } - else { /* SMHOLDERTYPE(ctype) == Container */ + else if(SMHOLDERTYPE(ctype) == Container){ char *prefix= ctype == CACert ? CACERTSTORELEADER : EMAILADDRLEADER; char tmp[MAILTMPLEN], *s, *t; @@ -495,6 +495,7 @@ smime_expunge_cert(WhichCerts ctype) } else removed = 0; + } else { /* unhandled case */ } if(removed > 0){ @@ -972,6 +973,7 @@ renew_cert_data(CertList **data, WhichCerts ctype) } } } + setup_certs_backup_by_type(ctype); } void @@ -1013,6 +1015,7 @@ smime_init(void) setup_storage_locations(); s_cert_store = get_ca_store(); + setup_certs_backup_by_type(CACert); OpenSSL_add_all_algorithms(); ERR_load_crypto_strings(); @@ -1080,7 +1083,7 @@ get_personal_certs(char *path) pc->name = cpystr(buf2); /* Try to load the key with an empty password */ - pc->key = load_key(pc, ""); + pc->key = load_key(pc, "", SM_NORMALCERT); pc->next = result; result = pc; @@ -1156,6 +1159,7 @@ setup_privatekey_storage(void) if(ps_global->smime->privatetype == Directory) ps_global->smime->personal_certs = get_personal_certs(path); } + setup_certs_backup_by_type(Private); } @@ -2030,7 +2034,7 @@ load_private_key(PERSONAL_CERT *pcert) ERR_clear_error(); - if(!(pcert->key = load_key(pcert, password))){ + if(!(pcert->key = load_key(pcert, password, SM_NORMALCERT))){ long err = ERR_get_error(); /* Couldn't load key... */ @@ -3586,12 +3590,21 @@ free_smime_struct(SMIME_STUFF_S **smime) if((*smime)->publiccertlist) free_certlist(&(*smime)->publiccertlist); + if((*smime)->backuppubliccertlist) + free_certlist(&(*smime)->backuppubliccertlist); + if((*smime)->cacertlist) free_certlist(&(*smime)->cacertlist); + if((*smime)->backupcacertlist) + free_certlist(&(*smime)->backupcacertlist); + if((*smime)->privatecertlist) free_certlist(&(*smime)->privatecertlist); + if((*smime)->backupprivatecertlist) + free_certlist(&(*smime)->backupprivatecertlist); + if((*smime)->publiccontent) fs_give((void **) &(*smime)->publiccontent); @@ -3606,6 +3619,14 @@ free_smime_struct(SMIME_STUFF_S **smime) (*smime)->personal_certs = NULL; } + if((*smime)->backuppersonal_certs){ + PERSONAL_CERT *pc; + + pc = (PERSONAL_CERT *) (*smime)->backuppersonal_certs; + free_personal_certs(&pc); + (*smime)->backuppersonal_certs = NULL; + } + if((*smime)->privatecontent) fs_give((void **) &(*smime)->privatecontent); diff --git a/pith/smkeys.c b/pith/smkeys.c index 437cba6f..9f6570c3 100644 --- a/pith/smkeys.c +++ b/pith/smkeys.c @@ -33,6 +33,7 @@ static char rcsid[] = "$Id: smkeys.c 1266 2009-07-14 18:39:12Z hubert@u.washingt #include "../pith/osdep/lstcmpnt.h" #include "../pith/util.h" #include "../pith/mailindx.h" +#include "../pith/readfile.h" #include "smkeys.h" #ifdef APPLEKEYCHAIN @@ -46,27 +47,228 @@ static char rcsid[] = "$Id: smkeys.c 1266 2009-07-14 18:39:12Z hubert@u.washingt /* internal prototypes */ static char *emailstrclean(char *string); static int mem_add_extra_cacerts(char *contents, X509_LOOKUP *lookup); -int compare_certs(const void *data1, const void *data2); +int compare_certs_by_name(const void *data1, const void *data2); +#define SMIME_BACKUP_DIR ".backup" +#define MAX_TRY_BACKUP 100 + +/* return value: 0 - success, -1 error + * Call this function after setting up paths in ps_global->smime + * and reading certificates names in certlist. + */ int -compare_certs(const void *data1, const void *data2) +setup_certs_backup_by_type(WhichCerts ctype) { - int rv; + int rv = 0; /* assume success */ + int len; + int i, done; + char *d; + char p[MAXPATH+1]; /* path to where the backup is */ + char buf[MAXPATH+1], buf2[MAXPATH+1]; + struct stat sbuf; + CertList *data, *cl; + DIR *dirp; + struct dirent *df; /* file in the directory */ + CertList *cert, *cl2; + X509 *x; + BIO *in; + + return rv; /* remove when this function is complete */ + + if(SMHOLDERTYPE(ctype) == Directory){ + d = PATHCERTDIR(ctype); + if(d != NULL){ + len = strlen(d) + strlen(S_FILESEP) + strlen(SMIME_BACKUP_DIR) + 1; + snprintf(p, MAXPATH, "%s%s%s", d, S_FILESEP, SMIME_BACKUP_DIR); + p[MAXPATH] = '\0'; + if(our_stat(p, &sbuf) < 0){ + if(our_mkpath(p, 0700) != 0) + return -1; + } else if((sbuf.st_mode & S_IFMT) != S_IFDIR){ + for(i = 0, done = 0; done == 0 && i < MAX_TRY_BACKUP; i++){ + snprintf(buf2, len+2, "%s%d", p, i); + if(our_stat(buf2, &sbuf) < 0){ + if(our_mkpath(buf2, 0700) == 0) + done++; + } + else if((sbuf.st_mode & S_IFMT) == S_IFDIR) + done++; + if(done){ + strncpy(p, buf2, MAXPATH); + p[MAXPATH] = '\0'; + } + } + if(done == 0) + return -1; + } + /* if we are here, we have a backup directory where to + * backup certificates/keys, so now we will go + * through the list of certificates and back them up + * if we need to. + */ + data = BACKUPDATACERT(ctype); + for(cl = DATACERT(ctype); cl; cl = cl->next){ + char clname[MAXPATH+1]; + + snprintf(clname, MAXPATH, "%s%s", cl->name, ctype == Private ? ".key" : ""); + clname[MAXPATH] = '\0'; + len = strlen(d) + strlen(clname) + 2; + if(len < MAXPATH){ + snprintf(buf, len, "%s%s%s", d, S_FILESEP, clname); + buf[sizeof(buf)-1] = '\0'; + len = strlen(p) + strlen(clname) + strlen(cl->data.md5) + 3; + if(len < MAXPATH){ + snprintf(buf2, len, "%s%s%s.%s", p, S_FILESEP, clname, cl->data.md5); + buf2[sizeof(buf2)-1] = '\0'; + done = 0; /* recycle done: it means we have a file that may be a certifificate*/ + if(stat(buf2, &sbuf) < 0){ + if (our_copy(buf2, buf) == 0) + done++; + } else if((sbuf.st_mode & S_IFMT) == S_IFREG) + done++; + + if(done){ + switch(ctype){ + case Public: + case CACert: + if((in = BIO_new_file(buf2, "r"))!=0){ + cert = fs_get(sizeof(CertList)); + memset((void *)cert, 0, sizeof(CertList)); + cert->x509_cert = PEM_read_bio_X509(in, NULL, NULL, NULL); + if(cl->data.date_from != NULL) + cert->data.date_from = cpystr(cl->data.date_from); + if(cl->data.date_to != NULL) + cert->data.date_to = cpystr(cl->data.date_to); + if(cl->data.md5 != NULL) + cert->data.md5 = cpystr(cl->data.md5); + snprintf(buf2, len, "%s.%s", cl->name, cl->data.md5); + buf2[sizeof(buf2)-1] = '\0'; + cert->name = cpystr(buf2); + if(data == NULL) + data = cert; + else{ + for (cl2 = data; cl2 && cl2->next; cl2 = cl2->next); + cl2->next = cert; + } + BIO_free(in); + } + break; + + case Private: break; + default: alpine_panic("Bad ctype (0)"); + } + } + } + } + } + /* if we are here, it means we just loaded the backup variable with + * a copy of the data that comes from the certlist not coming from + * backup. Now we are going to load the contents of the .backup + * directory. + */ + + /* Here is the plan: read the backup directory (in the variable "p") + * and attempt to add it. If already there, skip it; otherwise continue + */ + + if((dirp = opendir(p)) != NULL){ + while((df=readdir(dirp)) != NULL){ + if(df->d_name && *df->d_name == '.') /* no hidden files here */ + continue; + + /* make sure that we have a file */ + snprintf(buf2, sizeof(buf2), "%s%s%s", p, S_FILESEP, df->d_name); + buf2[sizeof(buf2)-1] = '\0'; + if(our_stat(buf2, &sbuf) == 0 + && (sbuf.st_mode & S_IFMT) != S_IFREG) + continue; + + /* make sure it is not already in the list */ + for(cl = data; cl; cl = cl->next) + if(strcmp(cl->name, df->d_name) == 0) + break; + if(cl != NULL) + continue; + + /* ok, if it is not in the list, and it is a certificate. Add it */ + switch(ctype){ + case Public: + case CACert: + if((in = BIO_new_file(buf2, "r"))!=0){ + x = PEM_read_bio_X509(in, NULL, NULL, NULL); + if(x && x->cert_info){ /* for now copy this information */ + cert = fs_get(sizeof(CertList)); + memset((void *)cert, 0, sizeof(CertList)); + cert->x509_cert = x; + cert->data.date_from = smime_get_date(x->cert_info->validity->notBefore); + cert->data.date_to = smime_get_date(x->cert_info->validity->notAfter); + get_fingerprint(x, EVP_md5(), buf, sizeof(buf), NULL); + cert->data.md5 = cpystr(buf); + cert->name = cpystr(df->d_name); + /* we will use the cert->data.md5 variable to find a backup + certificate, not the name */ + if(data == NULL) + data = cert; + else{ + for (cl2 = data; cl2 && cl2->next; cl2 = cl2->next); + cl2->next = cert; + } + } + BIO_free(in); + } + break; + + case Private: + /* here we must check it is a key of some cert....*/ + break; + + default: alpine_panic("Bad ctype (1)"); + } /* end switch */ + } + closedir(dirp); + } + + /* Now that we are here, we have all the information in the backup + * directory + */ + + switch(ctype){ + case Public : ps_global->smime->backuppubliccertlist = data; break; + case Private: ps_global->smime->backupprivatecertlist = data; break; + case CACert : ps_global->smime->backupcacertlist = data; break; + default : alpine_panic("Bad ctype (n)"); + } + } + } else if(SMHOLDERTYPE(ctype) == Container){ + + } /* else APPLEKEYCHAIN */ + return rv; +} + +int +compare_certs_by_name(const void *data1, const void *data2) +{ + int rv, i, j; char *s; - + CertList *cl1 = *(CertList **) data1; CertList *cl2 = *(CertList **) data2; - - if((s = strchr(cl1->name, '@')) != NULL) + + i = j = -1; + if((s = strchr(cl1->name, '@')) != NULL){ + i = s - cl1->name; *s = '\0'; - - if((s = strchr(cl2->name, '@')) != NULL) + } + + if((s = strchr(cl2->name, '@')) != NULL){ + j = s - cl2->name; *s = '\0'; - + } + if((rv = strucmp(cl1->name, cl2->name)) == 0) - rv = strucmp(cl1->name + strlen(cl1->name) + 1, cl2->name + strlen(cl2->name) + 1); - cl1->name[strlen(cl1->name)] = '@'; - cl2->name[strlen(cl2->name)] = '@'; + rv = strucmp(cl1->name + i + 1, cl2->name + j + 1); + if(i >= 0) cl1->name[i] = '@'; + if(j >= 0) cl2->name[j] = '@'; return rv; } @@ -78,6 +280,9 @@ resort_certificates(CertList **data, WhichCerts ctype) CertList **cll; char *s, *t; + if(cl == NULL) + return; + for(i = 0; cl; cl = cl->next, i++) if(ctype != Private){ /* ctype == Public or ctype == CACerts */ for(t = s = cl->name; t = strstr(s, ".crt"); s = t+1); @@ -87,34 +292,19 @@ resort_certificates(CertList **data, WhichCerts ctype) cll = fs_get(i*sizeof(CertList *)); for(cl = *data, i = 0; cl; cl = cl->next, i++) cll[i] = cl; - qsort((void *)cll, j, sizeof(CertList *), compare_certs); + qsort((void *)cll, j, sizeof(CertList *), compare_certs_by_name); for(i = 0; i < j - 1; i++){ cll[i]->next = cll[i+1]; if(ctype != Private) cll[i]->name[strlen(cll[i]->name)]= '.'; /* restore ".crt" part */ } + if(ctype != Private) + cll[j-1]->name[strlen(cll[j-1]->name)]= '.'; /* restore ".crt" part */ cll[j-1]->next = NULL; *data = cll[0]; } -/* given a certificate and an email address, add the - * extension and md5 key to the name. return an allocated - * name, freed by caller - */ -char * -smime_name(char *email, X509 *x, WhichCerts ctype) -{ - char bufx[256]; - char *rv; - - get_fingerprint(x, EVP_md5(), bufx, sizeof(bufx), NULL); - rv = fs_get(strlen(email) + 4 + 2 + strlen(bufx) + 1); - sprintf(rv, "%s%s.%s", email, EXTCERT(ctype), bufx); - - return rv; -} - void get_fingerprint(X509 *cert, const EVP_MD *type, char *buf, size_t maxLen, char *s) { @@ -186,6 +376,8 @@ smime_get_date(ASN1_GENERALIZEDTIME *tm) char date[MAILTMPLEN]; char buf[MAILTMPLEN]; char *m, *d, *t, *y, *z; + struct date smd; + struct tm smtm; (void) BIO_reset(mb); ASN1_UTCTIME_print(mb, tm); @@ -211,9 +403,17 @@ smime_get_date(ASN1_GENERALIZEDTIME *tm) snprintf(date, sizeof(date), "%s %s %s %s (%s)", d, m, y, t, z); date[sizeof(date)-1] = '\0'; - date_str((char *) date, iSDateS1, 1, buf, sizeof(buf), 0); - if(buf[strlen(buf) - 1] == '!') - buf[strlen(buf) - 1] = '\0'; + if(F_ON(F_DATES_TO_LOCAL,ps_global)){ + parse_date(convert_date_to_local(date), &smd); + memset(&smtm, 0, sizeof(smtm)); + smtm.tm_year = MIN(MAX(smd.year-1900, 0), 2000) % 100 - 1900; + smtm.tm_mon = MIN(MAX(smd.month-1, 0), 11); + smtm.tm_mday = MIN(MAX(smd.day, 1), 31); + our_strftime(buf, sizeof(buf), "%x", &smtm); + } + else + snprintf(buf, sizeof(buf), "%s/%s/%s", m, d, y + strlen(y) - 2); + buf[sizeof(buf)-1] = '\0'; return cpystr(buf); } @@ -334,7 +534,7 @@ get_ca_store(void) EVP_PKEY * -load_key(PERSONAL_CERT *pc, char *pass) +load_key(PERSONAL_CERT *pc, char *pass, int flag) { BIO *in; EVP_PKEY *key = NULL; @@ -853,7 +1053,7 @@ mem_to_personal_certs(char *contents) pc->name = cpystr(name); pc->keytext = keytext; /* a pointer into contents */ - pc->key = load_key(pc, ""); + pc->key = load_key(pc, "", SM_NORMALCERT); pc->next = result; result = pc; diff --git a/pith/smkeys.h b/pith/smkeys.h index 5214a369..37a92a90 100644 --- a/pith/smkeys.h +++ b/pith/smkeys.h @@ -43,6 +43,9 @@ typedef struct personal_cert { struct personal_cert *next; } PERSONAL_CERT; +/* flags that tell us where to look for certificates/keys */ +#define SM_NORMALCERT 0x1 /* look in normal user defined directory */ +#define SM_BACKUPCERT 0x2 /* look in backup directory */ /* exported protoypes */ int add_certs_in_dir(X509_LOOKUP *lookup, char *path, char *ext, CertList **cdata); @@ -51,7 +54,7 @@ PERSONAL_CERT *get_personal_certs(char *d); X509 *get_cert_for(char *email, WhichCerts ctype); void save_cert_for(char *email, X509 *cert, WhichCerts ctype); char **get_x509_subject_email(X509 *x); -EVP_PKEY *load_key(PERSONAL_CERT *pc, char *pass); +EVP_PKEY *load_key(PERSONAL_CERT *pc, char *pass, int flag); CertList *mem_to_certlist(char *contents, WhichCerts ctype); void add_to_end_of_certlist(CertList **cl, char *name, X509 *cert); void free_certlist(CertList **cl); @@ -60,10 +63,9 @@ void free_personal_certs(PERSONAL_CERT **pc); void get_fingerprint(X509 *cert, const EVP_MD *type, char *buf, size_t maxLen, char *s); int certlist_to_file(char *filename, CertList *certlist); int load_cert_for_key(char *pathdir, EVP_PKEY *pkey, char **certfile, X509 **pcert); -char *smime_name(char *email, X509 *x, WhichCerts ctype); char *smime_get_date(ASN1_GENERALIZEDTIME *tm); void resort_certificates(CertList **data, WhichCerts ctype); - +int setup_certs_backup_by_type(WhichCerts ctype); #endif /* PITH_SMKEYS_INCLUDED */ #endif /* SMIME */ diff --git a/pith/sort.c b/pith/sort.c index b18699e5..890204af 100644 --- a/pith/sort.c +++ b/pith/sort.c @@ -488,7 +488,7 @@ sort_sort_callback(MAILSTREAM *stream, long unsigned int *list, long unsigned in dprint((2, "sort_sort_callback\n")); if(mn_get_total(g_sort.msgmap) < nmsgs) - panic("Message count shrank after sort!"); + alpine_panic("Message count shrank after sort!"); /* copy ulongs to array of longs */ for(i = nmsgs; i > 0; i--) diff --git a/pith/stream.c b/pith/stream.c index 5201a440..e03c7748 100644 --- a/pith/stream.c +++ b/pith/stream.c @@ -1729,7 +1729,7 @@ pine_mail_partial_fetch_wrapper(MAILSTREAM *stream, long unsigned int msgno, * partial text. */ if(!str_to_free) - panic("Programmer botch: partial fetch attempt w/o string pointer"); + alpine_panic("Programmer botch: partial fetch attempt w/o string pointer"); else *str_to_free = (char *) new_text.data; } diff --git a/pith/string.c b/pith/string.c index 6ad1045a..2d7a02b2 100644 --- a/pith/string.c +++ b/pith/string.c @@ -2674,7 +2674,7 @@ add_escapes(char *src, char *quote_these_chars, int quoting_char, char *ans = NULL; if(!quote_these_chars) - panic("bad arg to add_escapes"); + alpine_panic("bad arg to add_escapes"); if(src){ char *q, *p, *qchar; diff --git a/pith/util.h b/pith/util.h index defbca6c..701ed061 100644 --- a/pith/util.h +++ b/pith/util.h @@ -56,7 +56,7 @@ int *cpyint(int); /* currently mandatory to implement stubs */ /* called when we detect a serious program error */ -void panic(char *); +void alpine_panic(char *); /* called when testing to see if panic state is in effect */ int panicking(void); |