-/*
- * This function is self explanatory.
- * (What can I say, I'm in a weird mood today...)
- */
-void remove_any_whitespace_to_the_left_or_right_of_at_symbol(char *name)
-{
- int i;
-
- for (i = 0; i < strlen(name); ++i) {
- if (name[i] == '@') {
- while (isspace(name[i - 1]) && i > 0) {
- strcpy(&name[i - 1], &name[i]);
- --i;
- }
- while (isspace(name[i + 1])) {
- strcpy(&name[i + 1], &name[i + 2]);
- }
- }
- }
-}
-
-
-/*
- * Aliasing for network mail.
- * (Error messages have been commented out, because this is a server.)
- */
-int alias(char *name)
-{ /* process alias and routing info for mail */
- struct CitContext *CCC = CC;
- FILE *fp;
- int a, i;
- char aaa[SIZ], bbb[SIZ];
- char *ignetcfg = NULL;
- char *ignetmap = NULL;
- int at = 0;
- char node[64];
- char testnode[64];
- char buf[SIZ];
-
- char original_name[256];
- safestrncpy(original_name, name, sizeof original_name);
-
- striplt(name);
- remove_any_whitespace_to_the_left_or_right_of_at_symbol(name);
- stripallbut(name, '<', '>');
-
- fp = fopen(file_mail_aliases, "r");
- if (fp == NULL) {
- fp = fopen("/dev/null", "r");
- }
- if (fp == NULL) {
- return (MES_ERROR);
- }
- strcpy(aaa, "");
- strcpy(bbb, "");
- while (fgets(aaa, sizeof aaa, fp) != NULL) {
- while (isspace(name[0]))
- strcpy(name, &name[1]);
- aaa[strlen(aaa) - 1] = 0;
- strcpy(bbb, "");
- for (a = 0; a < strlen(aaa); ++a) {
- if (aaa[a] == ',') {
- strcpy(bbb, &aaa[a + 1]);
- aaa[a] = 0;
- }
- }
- if (!strcasecmp(name, aaa))
- strcpy(name, bbb);
- }
- fclose(fp);
-
- /* Hit the Global Address Book */
- if (CtdlDirectoryLookup(aaa, name, sizeof aaa) == 0) {
- strcpy(name, aaa);
- }
-
- if (strcasecmp(original_name, name)) {
- MSG_syslog(LOG_INFO, "%s is being forwarded to %s\n", original_name, name);
- }
-
- /* Change "user @ xxx" to "user" if xxx is an alias for this host */
- for (a=0; a<strlen(name); ++a) {
- if (name[a] == '@') {
- if (CtdlHostAlias(&name[a+1]) == hostalias_localhost) {
- name[a] = 0;
- MSG_syslog(LOG_INFO, "Changed to <%s>\n", name);
- }
- }
- }
-
- /* determine local or remote type, see citadel.h */
- at = haschar(name, '@');
- if (at == 0) return(MES_LOCAL); /* no @'s - local address */
- if (at > 1) return(MES_ERROR); /* >1 @'s - invalid address */
- remove_any_whitespace_to_the_left_or_right_of_at_symbol(name);
-
- /* figure out the delivery mode */
- extract_token(node, name, 1, '@', sizeof node);
-
- /* If there are one or more dots in the nodename, we assume that it
- * is an FQDN and will attempt SMTP delivery to the Internet.
- */
- if (haschar(node, '.') > 0) {
- return(MES_INTERNET);
- }
-
- /* Otherwise we look in the IGnet maps for a valid Citadel node.
- * Try directly-connected nodes first...
- */
- ignetcfg = CtdlGetSysConfig(IGNETCFG);
- for (i=0; i<num_tokens(ignetcfg, '\n'); ++i) {
- extract_token(buf, ignetcfg, i, '\n', sizeof buf);
- extract_token(testnode, buf, 0, '|', sizeof testnode);
- if (!strcasecmp(node, testnode)) {
- free(ignetcfg);
- return(MES_IGNET);
- }
- }
- free(ignetcfg);
-
- /*
- * Then try nodes that are two or more hops away.
- */
- ignetmap = CtdlGetSysConfig(IGNETMAP);
- for (i=0; i<num_tokens(ignetmap, '\n'); ++i) {
- extract_token(buf, ignetmap, i, '\n', sizeof buf);
- extract_token(testnode, buf, 0, '|', sizeof testnode);
- if (!strcasecmp(node, testnode)) {
- free(ignetmap);
- return(MES_IGNET);
- }
- }
- free(ignetmap);
-
- /* If we get to this point it's an invalid node name */
- return (MES_ERROR);
-}
-
-
/*
* Back end for the MSGS command: output message number only.
*/
}
-char *qp_encode_email_addrs(char *source)
-{
- struct CitContext *CCC = CC;
- char *user, *node, *name;
- const char headerStr[] = "=?UTF-8?Q?";
- char *Encoded;
- char *EncodedName;
- char *nPtr;
- int need_to_encode = 0;
- long SourceLen;
- long EncodedMaxLen;
- long nColons = 0;
- long *AddrPtr;
- long *AddrUtf8;
- long nAddrPtrMax = 50;
- long nmax;
- int InQuotes = 0;
- int i, n;
-
- if (source == NULL) return source;
- if (IsEmptyStr(source)) return source;
- if (MessageDebugEnabled != 0) cit_backtrace();
- MSG_syslog(LOG_DEBUG, "qp_encode_email_addrs: [%s]\n", source);
-
- AddrPtr = malloc (sizeof (long) * nAddrPtrMax);
- AddrUtf8 = malloc (sizeof (long) * nAddrPtrMax);
- memset(AddrUtf8, 0, sizeof (long) * nAddrPtrMax);
- *AddrPtr = 0;
- i = 0;
- while (!IsEmptyStr (&source[i])) {
- if (nColons >= nAddrPtrMax){
- long *ptr;
-
- ptr = (long *) malloc(sizeof (long) * nAddrPtrMax * 2);
- memcpy (ptr, AddrPtr, sizeof (long) * nAddrPtrMax);
- free (AddrPtr), AddrPtr = ptr;
-
- ptr = (long *) malloc(sizeof (long) * nAddrPtrMax * 2);
- memset(&ptr[nAddrPtrMax], 0,
- sizeof (long) * nAddrPtrMax);
-
- memcpy (ptr, AddrUtf8, sizeof (long) * nAddrPtrMax);
- free (AddrUtf8), AddrUtf8 = ptr;
- nAddrPtrMax *= 2;
- }
- if (((unsigned char) source[i] < 32) ||
- ((unsigned char) source[i] > 126)) {
- need_to_encode = 1;
- AddrUtf8[nColons] = 1;
- }
- if (source[i] == '"')
- InQuotes = !InQuotes;
- if (!InQuotes && source[i] == ',') {
- AddrPtr[nColons] = i;
- nColons++;
- }
- i++;
- }
- if (need_to_encode == 0) {
- free(AddrPtr);
- free(AddrUtf8);
- return source;
- }
-
- SourceLen = i;
- EncodedMaxLen = nColons * (sizeof(headerStr) + 3) + SourceLen * 3;
- Encoded = (char*) malloc (EncodedMaxLen);
-
- for (i = 0; i < nColons; i++)
- source[AddrPtr[i]++] = '\0';
- /* TODO: if libidn, this might get larger*/
- user = malloc(SourceLen + 1);
- node = malloc(SourceLen + 1);
- name = malloc(SourceLen + 1);
-
- nPtr = Encoded;
- *nPtr = '\0';
- for (i = 0; i < nColons && nPtr != NULL; i++) {
- nmax = EncodedMaxLen - (nPtr - Encoded);
- if (AddrUtf8[i]) {
- process_rfc822_addr(&source[AddrPtr[i]],
- user,
- node,
- name);
- /* TODO: libIDN here ! */
- if (IsEmptyStr(name)) {
- n = snprintf(nPtr, nmax,
- (i==0)?"%s@%s" : ",%s@%s",
- user, node);
- }
- else {
- EncodedName = rfc2047encode(name, strlen(name));
- n = snprintf(nPtr, nmax,
- (i==0)?"%s <%s@%s>" : ",%s <%s@%s>",
- EncodedName, user, node);
- free(EncodedName);
- }
- }
- else {
- n = snprintf(nPtr, nmax,
- (i==0)?"%s" : ",%s",
- &source[AddrPtr[i]]);
- }
- if (n > 0 )
- nPtr += n;
- else {
- char *ptr, *nnPtr;
- ptr = (char*) malloc(EncodedMaxLen * 2);
- memcpy(ptr, Encoded, EncodedMaxLen);
- nnPtr = ptr + (nPtr - Encoded), nPtr = nnPtr;
- free(Encoded), Encoded = ptr;
- EncodedMaxLen *= 2;
- i--; /* do it once more with properly lengthened buffer */
- }
- }
- for (i = 0; i < nColons; i++)
- source[--AddrPtr[i]] = ',';
-
- free(user);
- free(node);
- free(name);
- free(AddrUtf8);
- free(AddrPtr);
- return Encoded;
-}
-
-
-/* If the last item in a list of recipients was truncated to a partial address,
- * remove it completely in order to avoid choking libSieve
- */
-void sanitize_truncated_recipient(char *str)
-{
- if (!str) return;
- if (num_tokens(str, ',') < 2) return;
-
- int len = strlen(str);
- if (len < 900) return;
- if (len > 998) str[998] = 0;
-
- char *cptr = strrchr(str, ',');
- if (!cptr) return;
-
- char *lptr = strchr(cptr, '<');
- char *rptr = strchr(cptr, '>');
-
- if ( (lptr) && (rptr) && (rptr > lptr) ) return;
-
- *cptr = 0;
-}
-
void OutputCtdlMsgHeaders(
struct CtdlMessage *TheMessage,
-/*
- * Validate recipients, count delivery types and errors, and handle aliasing
- * FIXME check for dupes!!!!!
- *
- * Returns 0 if all addresses are ok, ret->num_error = -1 if no addresses
- * were specified, or the number of addresses found invalid.
- *
- * Caller needs to free the result using free_recipients()
- */
-struct recptypes *validate_recipients(const char *supplied_recipients,
- const char *RemoteIdentifier,
- int Flags) {
- struct CitContext *CCC = CC;
- struct recptypes *ret;
- char *recipients = NULL;
- char *org_recp;
- char this_recp[256];
- char this_recp_cooked[256];
- char append[SIZ];
- long len;
- int num_recps = 0;
- int i, j;
- int mailtype;
- int invalid;
- struct ctdluser tempUS;
- struct ctdlroom tempQR;
- struct ctdlroom tempQR2;
- int err = 0;
- char errmsg[SIZ];
- int in_quotes = 0;
-
- /* Initialize */
- ret = (struct recptypes *) malloc(sizeof(struct recptypes));
- if (ret == NULL) return(NULL);
-
- /* Set all strings to null and numeric values to zero */
- memset(ret, 0, sizeof(struct recptypes));
-
- if (supplied_recipients == NULL) {
- recipients = strdup("");
- }
- else {
- recipients = strdup(supplied_recipients);
- }
-
- /* Allocate some memory. Yes, this allocates 500% more memory than we will
- * actually need, but it's healthier for the heap than doing lots of tiny
- * realloc() calls instead.
- */
- len = strlen(recipients) + 1024;
- ret->errormsg = malloc(len);
- ret->recp_local = malloc(len);
- ret->recp_internet = malloc(len);
- ret->recp_ignet = malloc(len);
- ret->recp_room = malloc(len);
- ret->display_recp = malloc(len);
- ret->recp_orgroom = malloc(len);
- org_recp = malloc(len);
-
- ret->errormsg[0] = 0;
- ret->recp_local[0] = 0;
- ret->recp_internet[0] = 0;
- ret->recp_ignet[0] = 0;
- ret->recp_room[0] = 0;
- ret->recp_orgroom[0] = 0;
- ret->display_recp[0] = 0;
-
- ret->recptypes_magic = RECPTYPES_MAGIC;
-
- /* Change all valid separator characters to commas */
- for (i=0; !IsEmptyStr(&recipients[i]); ++i) {
- if ((recipients[i] == ';') || (recipients[i] == '|')) {
- recipients[i] = ',';
- }
- }
-
- /* Now start extracting recipients... */
-
- while (!IsEmptyStr(recipients)) {
- for (i=0; i<=strlen(recipients); ++i) {
- if (recipients[i] == '\"') in_quotes = 1 - in_quotes;
- if ( ( (recipients[i] == ',') && (!in_quotes) ) || (recipients[i] == 0) ) {
- safestrncpy(this_recp, recipients, i+1);
- this_recp[i] = 0;
- if (recipients[i] == ',') {
- strcpy(recipients, &recipients[i+1]);
- }
- else {
- strcpy(recipients, "");
- }
- break;
- }
- }
-
- striplt(this_recp);
- if (IsEmptyStr(this_recp))
- break;
- MSG_syslog(LOG_DEBUG, "Evaluating recipient #%d: %s\n", num_recps, this_recp);
- ++num_recps;
-
- strcpy(org_recp, this_recp);
- alias(this_recp);
- alias(this_recp);
- mailtype = alias(this_recp);
-
- for (j = 0; !IsEmptyStr(&this_recp[j]); ++j) {
- if (this_recp[j]=='_') {
- this_recp_cooked[j] = ' ';
- }
- else {
- this_recp_cooked[j] = this_recp[j];
- }
- }
- this_recp_cooked[j] = '\0';
- invalid = 0;
- errmsg[0] = 0;
- switch(mailtype) {
- case MES_LOCAL:
- if (!strcasecmp(this_recp, "sysop")) {
- ++ret->num_room;
- strcpy(this_recp, config.c_aideroom);
- if (!IsEmptyStr(ret->recp_room)) {
- strcat(ret->recp_room, "|");
- }
- strcat(ret->recp_room, this_recp);
- }
- else if ( (!strncasecmp(this_recp, "room_", 5))
- && (!CtdlGetRoom(&tempQR, &this_recp_cooked[5])) ) {
-
- /* Save room so we can restore it later */
- tempQR2 = CCC->room;
- CCC->room = tempQR;
-
- /* Check permissions to send mail to this room */
- err = CtdlDoIHavePermissionToPostInThisRoom(
- errmsg,
- sizeof errmsg,
- RemoteIdentifier,
- Flags,
- 0 /* 0 = not a reply */
- );
- if (err)
- {
- ++ret->num_error;
- invalid = 1;
- }
- else {
- ++ret->num_room;
- if (!IsEmptyStr(ret->recp_room)) {
- strcat(ret->recp_room, "|");
- }
- strcat(ret->recp_room, &this_recp_cooked[5]);
-
- if (!IsEmptyStr(ret->recp_orgroom)) {
- strcat(ret->recp_orgroom, "|");
- }
- strcat(ret->recp_orgroom, org_recp);
-
- }
-
- /* Restore room in case something needs it */
- CCC->room = tempQR2;
-
- }
- else if (CtdlGetUser(&tempUS, this_recp) == 0) {
- ++ret->num_local;
- strcpy(this_recp, tempUS.fullname);
- if (!IsEmptyStr(ret->recp_local)) {
- strcat(ret->recp_local, "|");
- }
- strcat(ret->recp_local, this_recp);
- }
- else if (CtdlGetUser(&tempUS, this_recp_cooked) == 0) {
- ++ret->num_local;
- strcpy(this_recp, tempUS.fullname);
- if (!IsEmptyStr(ret->recp_local)) {
- strcat(ret->recp_local, "|");
- }
- strcat(ret->recp_local, this_recp);
- }
- else {
- ++ret->num_error;
- invalid = 1;
- }
- break;
- case MES_INTERNET:
- /* Yes, you're reading this correctly: if the target
- * domain points back to the local system or an attached
- * Citadel directory, the address is invalid. That's
- * because if the address were valid, we would have
- * already translated it to a local address by now.
- */
- if (IsDirectory(this_recp, 0)) {
- ++ret->num_error;
- invalid = 1;
- }
- else {
- ++ret->num_internet;
- if (!IsEmptyStr(ret->recp_internet)) {
- strcat(ret->recp_internet, "|");
- }
- strcat(ret->recp_internet, this_recp);
- }
- break;
- case MES_IGNET:
- ++ret->num_ignet;
- if (!IsEmptyStr(ret->recp_ignet)) {
- strcat(ret->recp_ignet, "|");
- }
- strcat(ret->recp_ignet, this_recp);
- break;
- case MES_ERROR:
- ++ret->num_error;
- invalid = 1;
- break;
- }
- if (invalid) {
- if (IsEmptyStr(errmsg)) {
- snprintf(append, sizeof append, "Invalid recipient: %s", this_recp);
- }
- else {
- snprintf(append, sizeof append, "%s", errmsg);
- }
- if ( (strlen(ret->errormsg) + strlen(append) + 3) < SIZ) {
- if (!IsEmptyStr(ret->errormsg)) {
- strcat(ret->errormsg, "; ");
- }
- strcat(ret->errormsg, append);
- }
- }
- else {
- if (IsEmptyStr(ret->display_recp)) {
- strcpy(append, this_recp);
- }
- else {
- snprintf(append, sizeof append, ", %s", this_recp);
- }
- if ( (strlen(ret->display_recp)+strlen(append)) < SIZ) {
- strcat(ret->display_recp, append);
- }
- }
- }
- free(org_recp);
-
- if ((ret->num_local + ret->num_internet + ret->num_ignet +
- ret->num_room + ret->num_error) == 0) {
- ret->num_error = (-1);
- strcpy(ret->errormsg, "No recipients specified.");
- }
-
- MSGM_syslog(LOG_DEBUG, "validate_recipients()\n");
- MSG_syslog(LOG_DEBUG, " local: %d <%s>\n", ret->num_local, ret->recp_local);
- MSG_syslog(LOG_DEBUG, " room: %d <%s>\n", ret->num_room, ret->recp_room);
- MSG_syslog(LOG_DEBUG, " inet: %d <%s>\n", ret->num_internet, ret->recp_internet);
- MSG_syslog(LOG_DEBUG, " ignet: %d <%s>\n", ret->num_ignet, ret->recp_ignet);
- MSG_syslog(LOG_DEBUG, " error: %d <%s>\n", ret->num_error, ret->errormsg);
-
- free(recipients);
- return(ret);
-}
-
-
-/*
- * Destructor for struct recptypes
- */
-void free_recipients(struct recptypes *valid) {
-
- if (valid == NULL) {
- return;
- }
-
- if (valid->recptypes_magic != RECPTYPES_MAGIC) {
- struct CitContext *CCC = CC;
- MSGM_syslog(LOG_EMERG, "Attempt to call free_recipients() on some other data type!\n");
- abort();
- }
-
- if (valid->errormsg != NULL) free(valid->errormsg);
- if (valid->recp_local != NULL) free(valid->recp_local);
- if (valid->recp_internet != NULL) free(valid->recp_internet);
- if (valid->recp_ignet != NULL) free(valid->recp_ignet);
- if (valid->recp_room != NULL) free(valid->recp_room);
- if (valid->recp_orgroom != NULL) free(valid->recp_orgroom);
- if (valid->display_recp != NULL) free(valid->display_recp);
- if (valid->bounce_to != NULL) free(valid->bounce_to);
- if (valid->envelope_from != NULL) free(valid->envelope_from);
- if (valid->sending_room != NULL) free(valid->sending_room);
- free(valid);
-}
-
-
-
/*
* message entry - mode 0 (normal)
*/
}
-/*
- * Determine whether a given Internet address belongs to the current user
- */
-int CtdlIsMe(char *addr, int addr_buf_len)
-{
- struct recptypes *recp;
- int i;
-
- recp = validate_recipients(addr, NULL, 0);
- if (recp == NULL) return(0);
-
- if (recp->num_local == 0) {
- free_recipients(recp);
- return(0);
- }
-
- for (i=0; i<recp->num_local; ++i) {
- extract_token(addr, recp->recp_local, i, '|', addr_buf_len);
- if (!strcasecmp(addr, CC->user.fullname)) {
- free_recipients(recp);
- return(1);
- }
- }
-
- free_recipients(recp);
- return(0);
-}
-
-
-/*
- * Citadel protocol command to do the same
- */
-void cmd_isme(char *argbuf) {
- char addr[256];
-
- if (CtdlAccessCheck(ac_logged_in)) return;
- extract_token(addr, argbuf, 0, '|', sizeof addr);
-
- if (CtdlIsMe(addr, sizeof addr)) {
- cprintf("%d %s\n", CIT_OK, addr);
- }
- else {
- cprintf("%d Not you.\n", ERROR + ILLEGAL_VALUE);
- }
-
-}
-
/*****************************************************************************/
/* MODULE INITIALIZATION STUFF */
CtdlRegisterProtoHook(cmd_ent0, "ENT0", "Enter a message");
CtdlRegisterProtoHook(cmd_dele, "DELE", "Delete a message");
CtdlRegisterProtoHook(cmd_move, "MOVE", "Move or copy a message to another room");
- CtdlRegisterProtoHook(cmd_isme, "ISME", "Determine whether an email address belongs to a user");
}
/* return our Subversion id for the Log */