/* * This function is called by the XMPP service's async loop. * If the client session has instant messages waiting, it outputs * unsolicited XML stanzas containing them. */ void xmpp_output_incoming_messages(void) { struct ExpressMessage *ptr; char xmlbuf1[4096]; char xmlbuf2[4096]; while (CC->FirstExpressMessage != NULL) { begin_critical_section(S_SESSION_TABLE); ptr = CC->FirstExpressMessage; CC->FirstExpressMessage = CC->FirstExpressMessage->next; end_critical_section(S_SESSION_TABLE); cprintf("<message to=\"%s\" from=\"%s\" type=\"chat\">", xmlesc(xmlbuf1, XMPP->client_jid, sizeof xmlbuf1), xmlesc(xmlbuf2, ptr->sender_email, sizeof xmlbuf2) ); if (ptr->text != NULL) { striplt(ptr->text); cprintf("<body>%s</body>", xmlesc(xmlbuf1, ptr->text, sizeof xmlbuf1)); free(ptr->text); } cprintf("</message>"); free(ptr); } }
/* * USER cmd */ void cmd_user(char *cmdbuf) { char username[256]; int a; CON_syslog(LOG_DEBUG, "cmd_user(%s)\n", cmdbuf); extract_token(username, cmdbuf, 0, '|', sizeof username); CON_syslog(LOG_DEBUG, "username: %s\n", username); striplt(username); CON_syslog(LOG_DEBUG, "username: %s\n", username); a = CtdlLoginExistingUser(NULL, username); switch (a) { case login_already_logged_in: cprintf("%d Already logged in.\n", ERROR + ALREADY_LOGGED_IN); return; case login_too_many_users: cprintf("%d %s: " "Too many users are already online " "(maximum is %d)\n", ERROR + MAX_SESSIONS_EXCEEDED, CtdlGetConfigStr("c_nodename"), CtdlGetConfigInt("c_maxsessions")); return; case login_ok: cprintf("%d Password required for %s\n", MORE_DATA, CC->curr_user); return; case login_not_found: cprintf("%d %s not found.\n", ERROR + NO_SUCH_USER, username); return; default: cprintf("%d Internal error\n", ERROR + INTERNAL_ERROR); } }
/* * List users (searchstring may be empty to list all users) */ void cmd_list(char *cmdbuf) { char searchstring[256]; extract_token(searchstring, cmdbuf, 0, '|', sizeof searchstring); striplt(searchstring); cprintf("%d \n", LISTING_FOLLOWS); ForEachUser(ListThisUser, (void *)searchstring ); cprintf("000\n"); }
/* * Look for a particular header field in an RFC822 message text. If the * requested field is found, it is unfolded (if necessary) and returned to * the caller. The field name is stripped out, leaving only its contents. * The caller is responsible for freeing the returned buffer. If the requested * field is not present, or anything else goes wrong, it returns NULL. */ char *rfc822_fetch_field(const char *rfc822, const char *fieldname) { char *fieldbuf = NULL; const char *end_of_headers; const char *field_start; const char *ptr; char *cont; char fieldhdr[SIZ]; /* Should never happen, but sometimes we get stupid */ if (rfc822 == NULL) return(NULL); if (fieldname == NULL) return(NULL); snprintf(fieldhdr, sizeof fieldhdr, "%s:", fieldname); /* Locate the end of the headers, so we don't run past that point */ end_of_headers = cbmstrcasestr(rfc822, "\n\r\n"); if (end_of_headers == NULL) { end_of_headers = cbmstrcasestr(rfc822, "\n\n"); } if (end_of_headers == NULL) return (NULL); field_start = cbmstrcasestr(rfc822, fieldhdr); if (field_start == NULL) return(NULL); if (field_start > end_of_headers) return(NULL); fieldbuf = malloc(SIZ); strcpy(fieldbuf, ""); ptr = field_start; ptr = cmemreadline(ptr, fieldbuf, SIZ-strlen(fieldbuf) ); while ( (isspace(ptr[0])) && (ptr < end_of_headers) ) { strcat(fieldbuf, " "); cont = &fieldbuf[strlen(fieldbuf)]; ptr = cmemreadline(ptr, cont, SIZ-strlen(fieldbuf) ); striplt(cont); } strcpy(fieldbuf, &fieldbuf[strlen(fieldhdr)]); striplt(fieldbuf); return(fieldbuf); }
/* * MIME Parser callback for wiki_rev() * * The "filename" field will contain a memo field, which includes (among other things) * the uuid of this revision. After we hit the desired revision, we stop processing. * * The "content" filed will contain "diff" output suitable for applying via "patch" * to our temporary file. */ void wiki_rev_callback(char *name, char *filename, char *partnum, char *disp, void *content, char *cbtype, char *cbcharset, size_t length, char *encoding, char *cbid, void *cbuserdata) { struct HistoryEraserCallBackData *hecbd = (struct HistoryEraserCallBackData *)cbuserdata; char memo[1024]; char this_rev[256]; FILE *fp; char *ptr = NULL; char buf[1024]; /* Did a previous callback already indicate that we've reached our target uuid? * If so, don't process anything else. */ if (hecbd->done) { return; } CtdlDecodeBase64(memo, filename, strlen(filename)); extract_token(this_rev, memo, 0, '|', sizeof this_rev); striplt(this_rev); /* Perform the patch */ fp = popen(PATCH " -f -s -p0 -r /dev/null >/dev/null 2>/dev/null", "w"); if (fp) { /* Replace the filenames in the patch with the tempfilename we're actually tweaking */ fprintf(fp, "--- %s\n", hecbd->tempfilename); fprintf(fp, "+++ %s\n", hecbd->tempfilename); ptr = (char *)content; int linenum = 0; do { ++linenum; ptr = memreadline(ptr, buf, sizeof buf); if (*ptr != 0) { if (linenum <= 2) { /* skip the first two lines; they contain bogus filenames */ } else { fprintf(fp, "%s\n", buf); } } } while ((*ptr != 0) && (ptr < ((char*)content + length))); if (pclose(fp) != 0) { syslog(LOG_ERR, "pclose() returned an error - patch failed\n"); } } if (!strcasecmp(this_rev, hecbd->stop_when)) { /* Found our target rev. Tell any subsequent callbacks to suppress processing. */ syslog(LOG_DEBUG, "Target revision has been reached -- stop patching.\n"); hecbd->done = 1; } }
/* * Convert FN (Friendly Name) into N (Name) * * vname Supplied friendly-name * n Target buffer to store Name * vname_size Size of buffer */ void vcard_fn_to_n(char *vname, char *n, size_t vname_size) { char lastname[256]; char firstname[256]; char middlename[256]; char honorific_prefixes[256]; char honorific_suffixes[256]; char buf[256]; safestrncpy(buf, n, sizeof buf); /* Try to intelligently convert the screen name to a * fully expanded vCard name based on the number of * words in the name */ safestrncpy(lastname, "", sizeof lastname); safestrncpy(firstname, "", sizeof firstname); safestrncpy(middlename, "", sizeof middlename); safestrncpy(honorific_prefixes, "", sizeof honorific_prefixes); safestrncpy(honorific_suffixes, "", sizeof honorific_suffixes); /* Honorific suffixes */ if (num_tokens(buf, ',') > 1) { extract_token(honorific_suffixes, buf, (num_tokens(buf, ' ') - 1), ',', sizeof honorific_suffixes); remove_token(buf, (num_tokens(buf, ',') - 1), ','); } /* Find a last name */ extract_token(lastname, buf, (num_tokens(buf, ' ') - 1), ' ', sizeof lastname); remove_token(buf, (num_tokens(buf, ' ') - 1), ' '); /* Find honorific prefixes */ if (num_tokens(buf, ' ') > 2) { extract_token(honorific_prefixes, buf, 0, ' ', sizeof honorific_prefixes); remove_token(buf, 0, ' '); } /* Find a middle name */ if (num_tokens(buf, ' ') > 1) { extract_token(middlename, buf, (num_tokens(buf, ' ') - 1), ' ', sizeof middlename); remove_token(buf, (num_tokens(buf, ' ') - 1), ' '); } /* Anything left is probably the first name */ safestrncpy(firstname, buf, sizeof firstname); striplt(firstname); /* Compose the structured name */ snprintf(vname, vname_size, "%s;%s;%s;%s;%s", lastname, firstname, middlename, honorific_prefixes, honorific_suffixes); }
/* * Check to see if a META tag has overridden the declared MIME character set. * * charset Character set name (left unchanged if we don't do anything) * meta_http_equiv Content of the "http-equiv" portion of the META tag * meta_content Content of the "content" portion of the META tag */ void extract_charset_from_meta(char *charset, char *meta_http_equiv, char *meta_content) { char *ptr; char buf[64]; if (!charset) return; if (!meta_http_equiv) return; if (!meta_content) return; if (strcasecmp(meta_http_equiv, "Content-type")) return; ptr = strchr(meta_content, ';'); if (!ptr) return; safestrncpy(buf, ++ptr, sizeof buf); striplt(buf); if (!strncasecmp(buf, "charset=", 8)) { strcpy(charset, &buf[8]); /* * The brain-damaged webmail program in Microsoft Exchange declares * a charset of "unicode" when they really mean "UTF-8". GNU iconv * treats "unicode" as an alias for "UTF-16" so we have to manually * fix this here, otherwise messages generated in Exchange webmail * show up as a big pile of weird characters. */ if (!strcasecmp(charset, "unicode")) { strcpy(charset, "UTF-8"); } /* Remove wandering punctuation */ if ((ptr=strchr(charset, '\"'))) *ptr = 0; striplt(charset); } }
/* Return nonzero if the supplied address is in a domain we keep in * the directory */ int IsDirectory(char *addr, int allow_masq_domains) { char domain[256]; int h; extract_token(domain, addr, 1, '@', sizeof domain); striplt(domain); h = CtdlHostAlias(domain); if ( (h == hostalias_masq) && allow_masq_domains) return(1); if ( (h == hostalias_localhost) || (h == hostalias_directory) ) { return(1); } else { return(0); } }
/* * Remove the "charset=" attribute from a vCard property name * */ void remove_charset_attribute(char *strbuf) { int i, t; char compare[256]; t = num_tokens(strbuf, ';'); for (i=0; i<t; ++i) { extract_token(compare, strbuf, i, ';', sizeof compare); striplt(compare); if (!strncasecmp(compare, "charset=", 8)) { remove_token(strbuf, i, ';'); } } if (!IsEmptyStr(strbuf)) { if (strbuf[strlen(strbuf)-1] == ';') { strbuf[strlen(strbuf)-1] = 0; } } }
/* * Convert a structured name into a friendly name. Caller must free the * returned pointer. */ char *n_to_fn(char *value) { char *nnn = NULL; int i; nnn = malloc(strlen(value) + 10); strcpy(nnn, ""); extract_token(&nnn[strlen(nnn)] , value, 3, ';', 999); strcat(nnn, " "); extract_token(&nnn[strlen(nnn)] , value, 1, ';', 999); strcat(nnn, " "); extract_token(&nnn[strlen(nnn)] , value, 2, ';', 999); strcat(nnn, " "); extract_token(&nnn[strlen(nnn)] , value, 0, ';', 999); strcat(nnn, " "); extract_token(&nnn[strlen(nnn)] , value, 4, ';', 999); strcat(nnn, " "); for (i=0; i<strlen(nnn); ++i) { if (!strncmp(&nnn[i], " ", 2)) strcpy(&nnn[i], &nnn[i+1]); } striplt(nnn); return(nnn); }
/* * Before allowing a wiki page save to execute, we have to perform version control. * This involves fetching the old version of the page if it exists. */ int wiki_upload_beforesave(struct CtdlMessage *msg, recptypes *recp) { struct CitContext *CCC = CC; long old_msgnum = (-1L); struct CtdlMessage *old_msg = NULL; long history_msgnum = (-1L); struct CtdlMessage *history_msg = NULL; char diff_old_filename[PATH_MAX]; char diff_new_filename[PATH_MAX]; char diff_out_filename[PATH_MAX]; char diff_cmd[PATH_MAX]; FILE *fp; int rv; char history_page[1024]; long history_page_len; char boundary[256]; char prefixed_boundary[258]; char buf[1024]; char *diffbuf = NULL; size_t diffbuf_len = 0; char *ptr = NULL; long newmsgid; StrBuf *msgidbuf; if (!CCC->logged_in) return(0); /* Only do this if logged in. */ /* Is this a room with a Wiki in it, don't run this hook. */ if ((CCC->room.QRdefaultview != VIEW_WIKI) && (CCC->room.QRdefaultview != VIEW_WIKIMD)) { return(0); } /* If this isn't a MIME message, don't bother. */ if (msg->cm_format_type != 4) return(0); /* If there's no EUID we can't do this. Reject the post. */ if (CM_IsEmpty(msg, eExclusiveID)) return(1); newmsgid = get_new_message_number(); msgidbuf = NewStrBuf(); StrBufPrintf(msgidbuf, "%08lX-%08lX@%s/%s", (long unsigned int) time(NULL), (long unsigned int) newmsgid, CtdlGetConfigStr("c_fqdn"), msg->cm_fields[eExclusiveID] ); CM_SetAsFieldSB(msg, emessageId, &msgidbuf); history_page_len = snprintf(history_page, sizeof history_page, "%s_HISTORY_", msg->cm_fields[eExclusiveID]); /* Make sure we're saving a real wiki page rather than a wiki history page. * This is important in order to avoid recursing infinitely into this hook. */ if ( (msg->cm_lengths[eExclusiveID] >= 9) && (!strcasecmp(&msg->cm_fields[eExclusiveID][msg->cm_lengths[eExclusiveID]-9], "_HISTORY_")) ) { syslog(LOG_DEBUG, "History page not being historied\n"); return(0); } /* If there's no message text, obviously this is all b0rken and shouldn't happen at all */ if (CM_IsEmpty(msg, eMesageText)) return(0); /* Set the message subject identical to the page name */ CM_CopyField(msg, eMsgSubject, eExclusiveID); /* See if we can retrieve the previous version. */ old_msgnum = CtdlLocateMessageByEuid(msg->cm_fields[eExclusiveID], &CCC->room); if (old_msgnum > 0L) { old_msg = CtdlFetchMessage(old_msgnum, 1, 1); } else { old_msg = NULL; } if ((old_msg != NULL) && (CM_IsEmpty(old_msg, eMesageText))) { /* old version is corrupt? */ CM_Free(old_msg); old_msg = NULL; } /* If no changes were made, don't bother saving it again */ if ((old_msg != NULL) && (!strcmp(msg->cm_fields[eMesageText], old_msg->cm_fields[eMesageText]))) { CM_Free(old_msg); return(1); } /* * Generate diffs */ CtdlMakeTempFileName(diff_old_filename, sizeof diff_old_filename); CtdlMakeTempFileName(diff_new_filename, sizeof diff_new_filename); CtdlMakeTempFileName(diff_out_filename, sizeof diff_out_filename); if (old_msg != NULL) { fp = fopen(diff_old_filename, "w"); rv = fwrite(old_msg->cm_fields[eMesageText], old_msg->cm_lengths[eMesageText], 1, fp); fclose(fp); CM_Free(old_msg); } fp = fopen(diff_new_filename, "w"); rv = fwrite(msg->cm_fields[eMesageText], msg->cm_lengths[eMesageText], 1, fp); fclose(fp); snprintf(diff_cmd, sizeof diff_cmd, DIFF " -u %s %s >%s", diff_new_filename, ((old_msg != NULL) ? diff_old_filename : "/dev/null"), diff_out_filename ); syslog(LOG_DEBUG, "diff cmd: %s", diff_cmd); rv = system(diff_cmd); syslog(LOG_DEBUG, "diff cmd returned %d", rv); diffbuf_len = 0; diffbuf = NULL; fp = fopen(diff_out_filename, "r"); if (fp == NULL) { fp = fopen("/dev/null", "r"); } if (fp != NULL) { fseek(fp, 0L, SEEK_END); diffbuf_len = ftell(fp); fseek(fp, 0L, SEEK_SET); diffbuf = malloc(diffbuf_len + 1); fread(diffbuf, diffbuf_len, 1, fp); diffbuf[diffbuf_len] = '\0'; fclose(fp); } syslog(LOG_DEBUG, "diff length is "SIZE_T_FMT" bytes", diffbuf_len); unlink(diff_old_filename); unlink(diff_new_filename); unlink(diff_out_filename); /* Determine whether this was a bogus (empty) edit */ if ((diffbuf_len = 0) && (diffbuf != NULL)) { free(diffbuf); diffbuf = NULL; } if (diffbuf == NULL) { return(1); /* No changes at all? Abandon the post entirely! */ } /* Now look for the existing edit history */ history_msgnum = CtdlLocateMessageByEuid(history_page, &CCC->room); history_msg = NULL; if (history_msgnum > 0L) { history_msg = CtdlFetchMessage(history_msgnum, 1, 1); } /* Create a new history message if necessary */ if (history_msg == NULL) { char *buf; long len; history_msg = malloc(sizeof(struct CtdlMessage)); memset(history_msg, 0, sizeof(struct CtdlMessage)); history_msg->cm_magic = CTDLMESSAGE_MAGIC; history_msg->cm_anon_type = MES_NORMAL; history_msg->cm_format_type = FMT_RFC822; CM_SetField(history_msg, eAuthor, HKEY("Citadel")); if (!IsEmptyStr(CCC->room.QRname)){ CM_SetField(history_msg, eRecipient, CCC->room.QRname, strlen(CCC->room.QRname)); } CM_SetField(history_msg, eExclusiveID, history_page, history_page_len); CM_SetField(history_msg, eMsgSubject, history_page, history_page_len); CM_SetField(history_msg, eSuppressIdx, HKEY("1")); /* suppress full text indexing */ snprintf(boundary, sizeof boundary, "Citadel--Multipart--%04x--%08lx", getpid(), time(NULL)); buf = (char*) malloc(1024); len = snprintf(buf, 1024, "Content-type: multipart/mixed; boundary=\"%s\"\n\n" "This is a Citadel wiki history encoded as multipart MIME.\n" "Each part is comprised of a diff script representing one change set.\n" "\n" "--%s--\n", boundary, boundary ); CM_SetAsField(history_msg, eMesageText, &buf, len); } /* Update the history message (regardless of whether it's new or existing) */ /* Remove the Message-ID from the old version of the history message. This will cause a brand * new one to be generated, avoiding an uninitentional hit of the loop zapper when we replicate. */ CM_FlushField(history_msg, emessageId); /* Figure out the boundary string. We do this even when we generated the * boundary string in the above code, just to be safe and consistent. */ *boundary = '\0'; ptr = history_msg->cm_fields[eMesageText]; do { ptr = memreadline(ptr, buf, sizeof buf); if (*ptr != 0) { striplt(buf); if (!IsEmptyStr(buf) && (!strncasecmp(buf, "Content-type:", 13))) { if ( (bmstrcasestr(buf, "multipart") != NULL) && (bmstrcasestr(buf, "boundary=") != NULL) ) { safestrncpy(boundary, bmstrcasestr(buf, "\""), sizeof boundary); char *qu; qu = strchr(boundary, '\"'); if (qu) { strcpy(boundary, ++qu); } qu = strchr(boundary, '\"'); if (qu) { *qu = 0; } } } } } while ( (IsEmptyStr(boundary)) && (*ptr != 0) ); /* * Now look for the first boundary. That is where we need to insert our fun. */ if (!IsEmptyStr(boundary)) { char *MsgText; long MsgTextLen; time_t Now = time(NULL); snprintf(prefixed_boundary, sizeof(prefixed_boundary), "--%s", boundary); CM_GetAsField(history_msg, eMesageText, &MsgText, &MsgTextLen); ptr = bmstrcasestr(MsgText, prefixed_boundary); if (ptr != NULL) { StrBuf *NewMsgText; char uuid[64]; char memo[512]; long memolen; char encoded_memo[1024]; NewMsgText = NewStrBufPlain(NULL, MsgTextLen + diffbuf_len + 1024); generate_uuid(uuid); memolen = snprintf(memo, sizeof(memo), "%s|%ld|%s|%s", uuid, Now, CCC->user.fullname, CtdlGetConfigStr("c_nodename")); memolen = CtdlEncodeBase64(encoded_memo, memo, memolen, 0); StrBufAppendBufPlain(NewMsgText, HKEY("--"), 0); StrBufAppendBufPlain(NewMsgText, boundary, -1, 0); StrBufAppendBufPlain( NewMsgText, HKEY("\n" "Content-type: text/plain\n" "Content-Disposition: inline; filename=\""), 0); StrBufAppendBufPlain(NewMsgText, encoded_memo, memolen, 0); StrBufAppendBufPlain( NewMsgText, HKEY("\"\n" "Content-Transfer-Encoding: 8bit\n" "\n"), 0); StrBufAppendBufPlain(NewMsgText, diffbuf, diffbuf_len, 0); StrBufAppendBufPlain(NewMsgText, HKEY("\n"), 0); StrBufAppendBufPlain(NewMsgText, ptr, MsgTextLen - (ptr - MsgText), 0); free(MsgText); CM_SetAsFieldSB(history_msg, eMesageText, &NewMsgText); } else { CM_SetAsField(history_msg, eMesageText, &MsgText, MsgTextLen); } CM_SetFieldLONG(history_msg, eTimestamp, Now); CtdlSubmitMsg(history_msg, NULL, "", 0); } else { syslog(LOG_ALERT, "Empty boundary string in history message. No history!\n"); } free(diffbuf); CM_Free(history_msg); return(0); }
/* * Fetch a specific revision of a wiki page. The "operation" string may be set to "fetch" in order * to simply fetch the desired revision and store it in a temporary location for viewing, or "revert" * to revert the currently active page to that revision. */ void wiki_rev(char *pagename, char *rev, char *operation) { struct CitContext *CCC = CC; int r; char history_page_name[270]; long msgnum; char temp[PATH_MAX]; struct CtdlMessage *msg; FILE *fp; struct HistoryEraserCallBackData hecbd; long len = 0L; int rv; r = CtdlDoIHavePermissionToReadMessagesInThisRoom(); if (r != om_ok) { if (r == om_not_logged_in) { cprintf("%d Not logged in.\n", ERROR + NOT_LOGGED_IN); } else { cprintf("%d An unknown error has occurred.\n", ERROR); } return; } if (!strcasecmp(operation, "revert")) { r = CtdlDoIHavePermissionToPostInThisRoom(temp, sizeof temp, NULL, POST_LOGGED_IN, 0); if (r != 0) { cprintf("%d %s\n", r, temp); return; } } /* Begin by fetching the current version of the page. We're going to patch * backwards through the diffs until we get the one we want. */ msgnum = CtdlLocateMessageByEuid(pagename, &CCC->room); if (msgnum > 0L) { msg = CtdlFetchMessage(msgnum, 1, 1); } else { msg = NULL; } if ((msg != NULL) && CM_IsEmpty(msg, eMesageText)) { CM_Free(msg); msg = NULL; } if (msg == NULL) { cprintf("%d Page '%s' was not found.\n", ERROR+MESSAGE_NOT_FOUND, pagename); return; } /* Output it to a temporary file */ CtdlMakeTempFileName(temp, sizeof temp); fp = fopen(temp, "w"); if (fp != NULL) { r = fwrite(msg->cm_fields[eMesageText], msg->cm_lengths[eMesageText], 1, fp); fclose(fp); } else { syslog(LOG_ALERT, "Cannot open %s: %s\n", temp, strerror(errno)); } CM_Free(msg); /* Get the revision history */ snprintf(history_page_name, sizeof history_page_name, "%s_HISTORY_", pagename); msgnum = CtdlLocateMessageByEuid(history_page_name, &CCC->room); if (msgnum > 0L) { msg = CtdlFetchMessage(msgnum, 1, 1); } else { msg = NULL; } if ((msg != NULL) && CM_IsEmpty(msg, eMesageText)) { CM_Free(msg); msg = NULL; } if (msg == NULL) { cprintf("%d Revision history for '%s' was not found.\n", ERROR+MESSAGE_NOT_FOUND, pagename); return; } /* Start patching backwards (newest to oldest) through the revision history until we * hit the revision uuid requested by the user. (The callback will perform each one.) */ memset(&hecbd, 0, sizeof(struct HistoryEraserCallBackData)); hecbd.tempfilename = temp; hecbd.stop_when = rev; striplt(hecbd.stop_when); mime_parser(CM_RANGE(msg, eMesageText), *wiki_rev_callback, NULL, NULL, (void *)&hecbd, 0); CM_Free(msg); /* Were we successful? */ if (hecbd.done == 0) { cprintf("%d Revision '%s' of page '%s' was not found.\n", ERROR + MESSAGE_NOT_FOUND, rev, pagename ); } /* We have the desired revision on disk. Now do something with it. */ else if ( (!strcasecmp(operation, "fetch")) || (!strcasecmp(operation, "revert")) ) { msg = malloc(sizeof(struct CtdlMessage)); memset(msg, 0, sizeof(struct CtdlMessage)); msg->cm_magic = CTDLMESSAGE_MAGIC; msg->cm_anon_type = MES_NORMAL; msg->cm_format_type = FMT_RFC822; fp = fopen(temp, "r"); if (fp) { char *msgbuf; fseek(fp, 0L, SEEK_END); len = ftell(fp); fseek(fp, 0L, SEEK_SET); msgbuf = malloc(len + 1); rv = fread(msgbuf, len, 1, fp); syslog(LOG_DEBUG, "did %d blocks of %ld bytes\n", rv, len); msgbuf[len] = '\0'; CM_SetAsField(msg, eMesageText, &msgbuf, len); fclose(fp); } if (len <= 0) { msgnum = (-1L); } else if (!strcasecmp(operation, "fetch")) { CM_SetField(msg, eAuthor, HKEY("Citadel")); CtdlCreateRoom(wwm, 5, "", 0, 1, 1, VIEW_BBS); /* Not an error if already exists */ msgnum = CtdlSubmitMsg(msg, NULL, wwm, 0); /* Store the revision here */ /* * WARNING: VILE SLEAZY HACK * This will avoid the 'message xxx is not in this room' security error, * but only if the client fetches the message we just generated immediately * without first trying to perform other fetch operations. */ if (CCC->cached_msglist != NULL) { free(CCC->cached_msglist); CCC->cached_msglist = NULL; CCC->cached_num_msgs = 0; } CCC->cached_msglist = malloc(sizeof(long)); if (CCC->cached_msglist != NULL) { CCC->cached_num_msgs = 1; CCC->cached_msglist[0] = msgnum; } } else if (!strcasecmp(operation, "revert")) { CM_SetFieldLONG(msg, eTimestamp, time(NULL)); if (!IsEmptyStr(CCC->user.fullname)) { CM_SetField(msg, eAuthor, CCC->user.fullname, strlen(CCC->user.fullname)); } if (!IsEmptyStr(CCC->cs_inet_email)) { CM_SetField(msg, erFc822Addr, CCC->cs_inet_email, strlen(CCC->cs_inet_email)); } if (!IsEmptyStr(CCC->room.QRname)) { CM_SetField(msg, eOriginalRoom, CCC->room.QRname, strlen(CCC->room.QRname)); } CM_SetField(msg, eNodeName, CtdlGetConfigStr("c_nodename"), strlen(CtdlGetConfigStr("c_nodename"))); if (!IsEmptyStr(pagename)) { CM_SetField(msg, eExclusiveID, pagename, strlen(pagename)); } msgnum = CtdlSubmitMsg(msg, NULL, "", 0); /* Replace the current revision */ } else { /* Theoretically it is impossible to get here, but throw an error anyway */ msgnum = (-1L); } CM_Free(msg); if (msgnum >= 0L) { cprintf("%d %ld\n", CIT_OK, msgnum); /* Give the client a msgnum */ } else { cprintf("%d Error %ld has occurred.\n", ERROR+INTERNAL_ERROR, msgnum); } } /* We did all this work for nothing. Express anguish to the caller. */ else { cprintf("%d An unknown operation was requested.\n", ERROR+CMD_NOT_SUPPORTED); } unlink(temp); return; }
/* * Harvest any email addresses that someone might want to have in their * "collected addresses" book. */ char *harvest_collected_addresses(struct CtdlMessage *msg) { char *coll = NULL; char addr[256]; char user[256], node[256], name[256]; int is_harvestable; int i, j, h; eMsgField field = 0; if (msg == NULL) return(NULL); is_harvestable = 1; strcpy(addr, ""); if (!CM_IsEmpty(msg, eAuthor)) { strcat(addr, msg->cm_fields[eAuthor]); } if (!CM_IsEmpty(msg, erFc822Addr)) { strcat(addr, " <"); strcat(addr, msg->cm_fields[erFc822Addr]); strcat(addr, ">"); if (IsDirectory(msg->cm_fields[erFc822Addr], 0)) { is_harvestable = 0; } } if (is_harvestable) { coll = strdup(addr); } else { coll = strdup(""); } if (coll == NULL) return(NULL); /* Scan both the R (To) and Y (CC) fields */ for (i = 0; i < 2; ++i) { if (i == 0) field = eRecipient; if (i == 1) field = eCarbonCopY; if (!CM_IsEmpty(msg, field)) { for (j=0; j<num_tokens(msg->cm_fields[field], ','); ++j) { extract_token(addr, msg->cm_fields[field], j, ',', sizeof addr); if (strstr(addr, "=?") != NULL) utf8ify_rfc822_string(addr); process_rfc822_addr(addr, user, node, name); h = CtdlHostAlias(node); if ( (h != hostalias_localhost) && (h != hostalias_directory) ) { coll = realloc(coll, strlen(coll) + strlen(addr) + 4); if (coll == NULL) return(NULL); if (!IsEmptyStr(coll)) { strcat(coll, ","); } striplt(addr); strcat(coll, addr); } } } } if (IsEmptyStr(coll)) { free(coll); return(NULL); } return(coll); }
/* * Sanitize and enhance an HTML message for display. * Also convert weird character sets to UTF-8 if necessary. * Also fixup img src="cid:..." type inline images to fetch the image * */ void output_html(const char *supplied_charset, int treat_as_wiki, int msgnum, StrBuf *Source, StrBuf *Target) { char buf[SIZ]; char *msg; char *ptr; char *msgstart; char *msgend; StrBuf *converted_msg; int buffer_length = 1; int line_length = 0; int content_length = 0; char new_window[SIZ]; int brak = 0; int alevel = 0; int scriptlevel = 0; int script_start_pos = (-1); int i; int linklen; char charset[128]; StrBuf *BodyArea = NULL; #ifdef HAVE_ICONV iconv_t ic = (iconv_t)(-1) ; char *ibuf; /* Buffer of characters to be converted */ char *obuf; /* Buffer for converted characters */ size_t ibuflen; /* Length of input buffer */ size_t obuflen; /* Length of output buffer */ char *osav; /* Saved pointer to output buffer */ #endif if (Target == NULL) Target = WC->WBuf; safestrncpy(charset, supplied_charset, sizeof charset); msg = strdup(""); sprintf(new_window, "<a target=\"%s\" href=", TARGET); if (Source == NULL) while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) { line_length = strlen(buf); buffer_length = content_length + line_length + 2; ptr = realloc(msg, buffer_length); if (ptr == NULL) { StrBufAppendPrintf(Target, "<b>"); StrBufAppendPrintf(Target, _("realloc() error! couldn't get %d bytes: %s"), buffer_length + 1, strerror(errno)); StrBufAppendPrintf(Target, "</b><br><br>\n"); while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) { /** flush */ } free(msg); return; } msg = ptr; strcpy(&msg[content_length], buf); content_length += line_length; strcpy(&msg[content_length], "\n"); content_length += 1; } else { content_length = StrLength(Source); free(msg); msg = (char*) ChrPtr(Source);/* TODO: remove cast */ buffer_length = content_length; } /** Do a first pass to isolate the message body */ ptr = msg + 1; msgstart = msg; msgend = &msg[content_length]; while (ptr < msgend) { /** Advance to next tag */ ptr = strchr(ptr, '<'); if ((ptr == NULL) || (ptr >= msgend)) break; ++ptr; if ((ptr == NULL) || (ptr >= msgend)) break; /* * Look for META tags. Some messages (particularly in * Asian locales) illegally declare a message's character * set in the HTML instead of in the MIME headers. This * is wrong but we have to work around it anyway. */ if (!strncasecmp(ptr, "META", 4)) { char *meta_start; char *meta_end; int meta_length; char *meta; char *meta_http_equiv; char *meta_content; char *spaceptr; meta_start = &ptr[4]; meta_end = strchr(ptr, '>'); if ((meta_end != NULL) && (meta_end <= msgend)) { meta_length = meta_end - meta_start + 1; meta = malloc(meta_length + 1); safestrncpy(meta, meta_start, meta_length); meta[meta_length] = 0; striplt(meta); if (!strncasecmp(meta, "HTTP-EQUIV=", 11)) { meta_http_equiv = strdup(&meta[11]); spaceptr = strchr(meta_http_equiv, ' '); if (spaceptr != NULL) { *spaceptr = 0; meta_content = strdup(++spaceptr); if (!strncasecmp(meta_content, "content=", 8)) { strcpy(meta_content, &meta_content[8]); stripquotes(meta_http_equiv); stripquotes(meta_content); extract_charset_from_meta(charset, meta_http_equiv, meta_content); } free(meta_content); } free(meta_http_equiv); } free(meta); } } /* * Any of these tags cause everything up to and including * the tag to be removed. */ if ( (!strncasecmp(ptr, "HTML", 4)) ||(!strncasecmp(ptr, "HEAD", 4)) ||(!strncasecmp(ptr, "/HEAD", 5)) ||(!strncasecmp(ptr, "BODY", 4)) ) { char *pBody = NULL; if (!strncasecmp(ptr, "BODY", 4)) { pBody = ptr; } ptr = strchr(ptr, '>'); if ((ptr == NULL) || (ptr >= msgend)) break; if ((pBody != NULL) && (ptr - pBody > 4)) { char* src; char *cid_start, *cid_end; *ptr = '\0'; pBody += 4; while ((isspace(*pBody)) && (pBody < ptr)) pBody ++; BodyArea = NewStrBufPlain(NULL, ptr - pBody); if (pBody < ptr) { src = strstr(pBody, "cid:"); if (src) { cid_start = src + 4; cid_end = cid_start; while ((*cid_end != '"') && !isspace(*cid_end) && (cid_end < ptr)) cid_end ++; /* copy tag and attributes up to src="cid: */ StrBufAppendBufPlain(BodyArea, pBody, src - pBody, 0); /* add in /webcit/mimepart/<msgno>/CID/ trailing / stops dumb URL filters getting excited */ StrBufAppendPrintf(BodyArea, "/webcit/mimepart/%d/",msgnum); StrBufAppendBufPlain(BodyArea, cid_start, cid_end - cid_start, 0); if (ptr - cid_end > 0) StrBufAppendBufPlain(BodyArea, cid_end + 1, ptr - cid_end, 0); } else StrBufAppendBufPlain(BodyArea, pBody, ptr - pBody, 0); } *ptr = '>'; } ++ptr; if ((ptr == NULL) || (ptr >= msgend)) break; msgstart = ptr; } /* * Any of these tags cause everything including and following * the tag to be removed. */ if ( (!strncasecmp(ptr, "/HTML", 5)) ||(!strncasecmp(ptr, "/BODY", 5)) ) { --ptr; msgend = ptr; strcpy(ptr, ""); } ++ptr; } if (msgstart > msg) { strcpy(msg, msgstart); } /* Now go through the message, parsing tags as necessary. */ converted_msg = NewStrBufPlain(NULL, content_length + 8192); /** Convert foreign character sets to UTF-8 if necessary. */ #ifdef HAVE_ICONV if ( (strcasecmp(charset, "us-ascii")) && (strcasecmp(charset, "UTF-8")) && (strcasecmp(charset, "")) ) { syslog(LOG_DEBUG, "Converting %s to UTF-8\n", charset); ctdl_iconv_open("UTF-8", charset, &ic); if (ic == (iconv_t)(-1) ) { syslog(LOG_WARNING, "%s:%d iconv_open() failed: %s\n", __FILE__, __LINE__, strerror(errno)); } } if (Source == NULL) { if (ic != (iconv_t)(-1) ) { ibuf = msg; ibuflen = content_length; obuflen = content_length + (content_length / 2) ; obuf = (char *) malloc(obuflen); osav = obuf; iconv(ic, &ibuf, &ibuflen, &obuf, &obuflen); content_length = content_length + (content_length / 2) - obuflen; osav[content_length] = 0; free(msg); msg = osav; iconv_close(ic); } } else { if (ic != (iconv_t)(-1) ) { StrBuf *Buf = NewStrBufPlain(NULL, StrLength(Source) + 8096);; StrBufConvert(Source, Buf, &ic); FreeStrBuf(&Buf); iconv_close(ic); msg = (char*)ChrPtr(Source); /* TODO: get rid of this. */ } } #endif /* * At this point, the message has been stripped down to * only the content inside the <BODY></BODY> tags, and has * been converted to UTF-8 if it was originally in a foreign * character set. The text is also guaranteed to be null * terminated now. */ if (converted_msg == NULL) { StrBufAppendPrintf(Target, "Error %d: %s<br>%s:%d", errno, strerror(errno), __FILE__, __LINE__); goto BAIL; } if (BodyArea != NULL) { StrBufAppendBufPlain(converted_msg, HKEY("<table "), 0); StrBufAppendBuf(converted_msg, BodyArea, 0); StrBufAppendBufPlain(converted_msg, HKEY(" width=\"100%\"><tr><td>"), 0); } ptr = msg; msgend = strchr(msg, 0); while (ptr < msgend) { /** Try to sanitize the html of any rogue scripts */ if (!strncasecmp(ptr, "<script", 7)) { if (scriptlevel == 0) { script_start_pos = StrLength(converted_msg); } ++scriptlevel; } if (!strncasecmp(ptr, "</script", 8)) { --scriptlevel; } /** * Change mailto: links to WebCit mail, by replacing the * link with one that points back to our mail room. Due to * the way we parse URL's, it'll even handle mailto: links * that have "?subject=" in them. */ if (!strncasecmp(ptr, "<a href=\"mailto:", 16)) { content_length += 64; StrBufAppendPrintf(converted_msg, "<a href=\"display_enter?force_room=_MAIL_?recp="); ptr = &ptr[16]; ++alevel; ++brak; } /** Make external links open in a separate window */ else if (!strncasecmp(ptr, "<a href=\"", 9)) { ++alevel; ++brak; if ( ((strchr(ptr, ':') < strchr(ptr, '/'))) && ((strchr(ptr, '/') < strchr(ptr, '>'))) ) { /* open external links to new window */ StrBufAppendPrintf(converted_msg, new_window); ptr = &ptr[8]; } else if ( (treat_as_wiki) && (strncasecmp(ptr, "<a href=\"wiki?", 14)) && (strncasecmp(ptr, "<a href=\"dotgoto?", 17)) && (strncasecmp(ptr, "<a href=\"knrooms?", 17)) ) { content_length += 64; StrBufAppendPrintf(converted_msg, "<a href=\"wiki?go="); StrBufUrlescAppend(converted_msg, WC->CurRoom.name, NULL); StrBufAppendPrintf(converted_msg, "?page="); ptr = &ptr[9]; } else { StrBufAppendPrintf(converted_msg, "<a href=\""); ptr = &ptr[9]; } } /** Fixup <img src="cid:... ...> to fetch the mime part */ else if (!strncasecmp(ptr, "<img ", 5)) { char *cid_start, *cid_end; char* tag_end=strchr(ptr,'>'); char* src; /* FIXME - handle this situation (maybe someone opened an <img cid... * and then ended the message) */ if (!tag_end) { syslog(LOG_DEBUG, "tag_end is null and ptr is:\n"); syslog(LOG_DEBUG, "%s\n", ptr); syslog(LOG_DEBUG, "Theoretical bytes remaining: %d\n", (int)(msgend - ptr)); } src=strstr(ptr, "src=\"cid:"); ++brak; if (src && isspace(*(src-1)) && tag_end && (cid_start=strchr(src,':')) && (cid_end=strchr(cid_start,'"')) && (cid_end < tag_end) ) { /* copy tag and attributes up to src="cid: */ StrBufAppendBufPlain(converted_msg, ptr, src - ptr, 0); cid_start++; /* add in /webcit/mimepart/<msgno>/CID/ trailing / stops dumb URL filters getting excited */ StrBufAppendPrintf(converted_msg, " src=\"/webcit/mimepart/%d/",msgnum); StrBufAppendBufPlain(converted_msg, cid_start, cid_end - cid_start, 0); StrBufAppendBufPlain(converted_msg, "/\"", -1, 0); ptr = cid_end+1; } StrBufAppendBufPlain(converted_msg, ptr, tag_end - ptr, 0); ptr = tag_end; } /** * Turn anything that looks like a URL into a real link, as long * as it's not inside a tag already */ else if ( (brak == 0) && (alevel == 0) && ( (!strncasecmp(ptr, "http://", 7)) || (!strncasecmp(ptr, "https://", 8)))) { /** Find the end of the link */ int strlenptr; linklen = 0; strlenptr = strlen(ptr); for (i=0; i<=strlenptr; ++i) { if ((ptr[i]==0) ||(isspace(ptr[i])) ||(ptr[i]==10) ||(ptr[i]==13) ||(ptr[i]=='(') ||(ptr[i]==')') ||(ptr[i]=='<') ||(ptr[i]=='>') ||(ptr[i]=='[') ||(ptr[i]==']') ||(ptr[i]=='"') ||(ptr[i]=='\'') ) linklen = i; /* did s.b. send us an entity? */ if (ptr[i] == '&') { if ((ptr[i+2] ==';') || (ptr[i+3] ==';') || (ptr[i+5] ==';') || (ptr[i+6] ==';') || (ptr[i+7] ==';')) linklen = i; } if (linklen > 0) break; } if (linklen > 0) { char *ltreviewptr; char *nbspreviewptr; char linkedchar; int len; len = linklen; linkedchar = ptr[len]; ptr[len] = '\0'; /* spot for some subject strings tinymce tends to give us. */ ltreviewptr = strchr(ptr, '<'); if (ltreviewptr != NULL) { *ltreviewptr = '\0'; linklen = ltreviewptr - ptr; } nbspreviewptr = strstr(ptr, " "); if (nbspreviewptr != NULL) { /* nbspreviewptr = '\0'; */ linklen = nbspreviewptr - ptr; } if (ltreviewptr != 0) *ltreviewptr = '<'; ptr[len] = linkedchar; content_length += (32 + linklen); StrBufAppendPrintf(converted_msg, "%s\"", new_window); StrBufAppendBufPlain(converted_msg, ptr, linklen, 0); StrBufAppendPrintf(converted_msg, "\">"); StrBufAppendBufPlain(converted_msg, ptr, linklen, 0); ptr += linklen; StrBufAppendPrintf(converted_msg, "</A>"); } } else { StrBufAppendBufPlain(converted_msg, ptr, 1, 0); ptr++; } if ((ptr >= msg) && (ptr <= msgend)) { /* * We need to know when we're inside a tag, * so we don't turn things that look like URL's into * links, when they're already links - or image sources. */ if ((ptr > msg) && (*(ptr-1) == '<')) { ++brak; } if ((ptr > msg) && (*(ptr-1) == '>')) { --brak; if ((scriptlevel == 0) && (script_start_pos >= 0)) { StrBufCutRight(converted_msg, StrLength(converted_msg) - script_start_pos); script_start_pos = (-1); } } if (!strncasecmp(ptr, "</A>", 3)) --alevel; } } if (BodyArea != NULL) { StrBufAppendBufPlain(converted_msg, HKEY("</td></tr></table>"), 0); FreeStrBuf(&BodyArea); } /** uncomment these two lines to override conversion */ /** memcpy(converted_msg, msg, content_length); */ /** output_length = content_length; */ /** Output our big pile of markup */ StrBufAppendBuf(Target, converted_msg, 0); BAIL: /** A little trailing vertical whitespace... */ StrBufAppendPrintf(Target, "<br><br>\n"); /** Now give back the memory */ FreeStrBuf(&converted_msg); if ((msg != NULL) && (Source == NULL)) free(msg); }
/* * 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; aaa[a] != '\0'; ++a) { if (aaa[a] == ',') { strcpy(bbb, &aaa[a + 1]); aaa[a] = 0; break; } } 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; name[a] != '\0'; ++a) { if (name[a] == '@') { if (CtdlHostAlias(&name[a+1]) == hostalias_localhost) { name[a] = 0; MSG_syslog(LOG_INFO, "Changed to <%s>\n", name); break; } } } /* 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); }
int smtp_resolve_recipients(SmtpOutMsg *Msg) { AsyncIO *IO = &Msg->IO; const char *ptr; char buf[1024]; int scan_done; int lp, rp; int i; EVNCS_syslog(LOG_DEBUG, "%s\n", __FUNCTION__); if ((Msg==NULL) || (Msg->MyQEntry == NULL) || (StrLength(Msg->MyQEntry->Recipient) == 0)) { return 0; } /* Parse out the host portion of the recipient address */ process_rfc822_addr(ChrPtr(Msg->MyQEntry->Recipient), Msg->user, Msg->node, Msg->name); EVNCS_syslog(LOG_DEBUG, "Attempting delivery to <%s> @ <%s> (%s)\n", Msg->user, Msg->node, Msg->name); /* If no envelope_from is supplied, extract one from the message */ Msg->envelope_from = ChrPtr(Msg->MyQItem->EnvelopeFrom); if ( (Msg->envelope_from == NULL) || (IsEmptyStr(Msg->envelope_from)) ) { Msg->mailfrom[0] = '\0'; scan_done = 0; ptr = ChrPtr(Msg->msgtext); do { if (ptr = cmemreadline(ptr, buf, sizeof buf), *ptr == 0) { scan_done = 1; } if (!strncasecmp(buf, "From:", 5)) { safestrncpy(Msg->mailfrom, &buf[5], sizeof Msg->mailfrom); striplt(Msg->mailfrom); for (i=0; Msg->mailfrom[i]; ++i) { if (!isprint(Msg->mailfrom[i])) { strcpy(&Msg->mailfrom[i], &Msg->mailfrom[i+1]); i=0; } } /* Strip out parenthesized names */ lp = (-1); rp = (-1); for (i=0; !IsEmptyStr(Msg->mailfrom + i); ++i) { if (Msg->mailfrom[i] == '(') lp = i; if (Msg->mailfrom[i] == ')') rp = i; } if ((lp>0)&&(rp>lp)) { strcpy(&Msg->mailfrom[lp-1], &Msg->mailfrom[rp+1]); } /* Prefer brokketized names */ lp = (-1); rp = (-1); for (i=0; !IsEmptyStr(Msg->mailfrom + i); ++i) { if (Msg->mailfrom[i] == '<') lp = i; if (Msg->mailfrom[i] == '>') rp = i; } if ( (lp>=0) && (rp>lp) ) { Msg->mailfrom[rp] = 0; memmove(Msg->mailfrom, &Msg->mailfrom[lp + 1], rp - lp); } scan_done = 1; } } while (scan_done == 0); if (IsEmptyStr(Msg->mailfrom)) strcpy(Msg->mailfrom, "*****@*****.**"); stripallbut(Msg->mailfrom, '<', '>'); Msg->envelope_from = Msg->mailfrom; } return 1; }
/* * Look up a user in the directory to see if this is an account that can be authenticated */ int CtdlTryUserLDAP(char *username, char *found_dn, int found_dn_size, char *fullname, int fullname_size, uid_t *uid, int lookup_based_on_username) { LDAP *ldserver = NULL; int i; LDAPMessage *search_result = NULL; LDAPMessage *entry = NULL; char searchstring[1024]; struct timeval tv; char **values; char *user_dn = NULL; if (fullname) safestrncpy(fullname, username, fullname_size); if (ctdl_ldap_initialize(&ldserver) != LDAP_SUCCESS) { return(errno); } ldap_set_option(ldserver, LDAP_OPT_PROTOCOL_VERSION, &ctdl_require_ldap_version); ldap_set_option(ldserver, LDAP_OPT_REFERRALS, (void *)LDAP_OPT_OFF); striplt(config.c_ldap_bind_dn); striplt(config.c_ldap_bind_pw); syslog(LOG_DEBUG, "LDAP bind DN: %s", config.c_ldap_bind_dn); i = ldap_simple_bind_s(ldserver, (!IsEmptyStr(config.c_ldap_bind_dn) ? config.c_ldap_bind_dn : NULL), (!IsEmptyStr(config.c_ldap_bind_pw) ? config.c_ldap_bind_pw : NULL) ); if (i != LDAP_SUCCESS) { syslog(LOG_ALERT, "LDAP: Cannot bind: %s (%d)", ldap_err2string(i), i); return(i); } tv.tv_sec = 10; tv.tv_usec = 0; if (config.c_auth_mode == AUTHMODE_LDAP_AD) { if (lookup_based_on_username != 0) snprintf(searchstring, sizeof(searchstring), "(displayName=%s)",username); else snprintf(searchstring, sizeof(searchstring), "(sAMAccountName=%s)", username); } else { if (lookup_based_on_username != 0) snprintf(searchstring, sizeof(searchstring), "(cn=%s)",username); else snprintf(searchstring, sizeof(searchstring), "(&(objectclass=posixAccount)(uid=%s))", username); } syslog(LOG_DEBUG, "LDAP search: %s", searchstring); (void) ldap_search_ext_s( ldserver, /* ld */ config.c_ldap_base_dn, /* base */ LDAP_SCOPE_SUBTREE, /* scope */ searchstring, /* filter */ NULL, /* attrs (all attributes) */ 0, /* attrsonly (attrs + values) */ NULL, /* serverctrls (none) */ NULL, /* clientctrls (none) */ &tv, /* timeout */ 1, /* sizelimit (1 result max) */ &search_result /* res */ ); /* Ignore the return value of ldap_search_ext_s(). Sometimes it returns an error even when * the search succeeds. Instead, we check to see whether search_result is still NULL. */ if (search_result == NULL) { syslog(LOG_DEBUG, "LDAP search: zero results were returned"); ldap_unbind(ldserver); return(2); } /* At this point we've got at least one result from our query. If there are multiple * results, we still only look at the first one. */ entry = ldap_first_entry(ldserver, search_result); if (entry) { user_dn = ldap_get_dn(ldserver, entry); if (user_dn) { syslog(LOG_DEBUG, "dn = %s", user_dn); } if (config.c_auth_mode == AUTHMODE_LDAP_AD) { values = ldap_get_values(ldserver, search_result, "displayName"); if (values) { if (values[0]) { if (fullname) safestrncpy(fullname, values[0], fullname_size); syslog(LOG_DEBUG, "displayName = %s", values[0]); } ldap_value_free(values); } } else { values = ldap_get_values(ldserver, search_result, "cn"); if (values) { if (values[0]) { if (fullname) safestrncpy(fullname, values[0], fullname_size); syslog(LOG_DEBUG, "cn = %s", values[0]); } ldap_value_free(values); } } /* If we know the username is the CN/displayName, we already set the uid*/ if (lookup_based_on_username==0) { if (config.c_auth_mode == AUTHMODE_LDAP_AD) { values = ldap_get_values(ldserver, search_result, "objectGUID"); if (values) { if (values[0]) { if (uid != NULL) { *uid = abs(HashLittle(values[0], strlen(values[0]))); syslog(LOG_DEBUG, "uid hashed from objectGUID = %d", *uid); } } ldap_value_free(values); } } else { values = ldap_get_values(ldserver, search_result, "uidNumber"); if (values) { if (values[0]) { syslog(LOG_DEBUG, "uidNumber = %s", values[0]); if (uid != NULL) { *uid = atoi(values[0]); } } ldap_value_free(values); } } } } /* free the results */ ldap_msgfree(search_result); /* unbind so we can go back in as the authenticating user */ ldap_unbind(ldserver); if (!user_dn) { syslog(LOG_DEBUG, "No such user was found."); return(4); } if (found_dn) safestrncpy(found_dn, user_dn, found_dn_size); ldap_memfree(user_dn); return(0); }
/* * 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() */ recptypes *validate_recipients(const char *supplied_recipients, const char *RemoteIdentifier, int Flags) { struct CitContext *CCC = CC; 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 = (recptypes *) malloc(sizeof(recptypes)); if (ret == NULL) return(NULL); /* Set all strings to null and numeric values to zero */ memset(ret, 0, sizeof(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, CtdlGetConfigStr("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); }
int main(int argc, char **argv) { char buf[1024]; char fromline[1024]; FILE *fp; int i; struct passwd *pw; int from_header = 0; int in_body = 0; int relh=0; int home=0; char relhome[PATH_MAX]=""; char ctdldir[PATH_MAX]=CTDLDIR; char *sp, *ep; char hostname[256]; char **recipients = NULL; int num_recipients = 0; int to_or_cc = 0; int read_recipients_from_headers = 0; char *add_these_recipients = NULL; for (i=1; i<argc; ++i) { if (!strcmp(argv[i], "-d")) { debug = 1; } else if (!strcmp(argv[i], "-t")) { read_recipients_from_headers = 1; } else if (argv[i][0] != '-') { ++num_recipients; recipients = realloc(recipients, (num_recipients * sizeof (char *))); recipients[num_recipients - 1] = strdup(argv[i]); } } /* TODO: should we be able to calculate relative dirs? */ calc_dirs_n_files(relh, home, relhome, ctdldir, 0); pw = getpwuid(getuid()); fp = tmpfile(); if (fp == NULL) return(errno); serv_sock = uds_connectsock(file_lmtp_socket); /* FIXME: if called as 'sendmail' connect to file_lmtp_unfiltered_socket */ serv_gets(buf); if (buf[0] != '2') { fprintf(stderr, "%s\n", &buf[4]); if (debug) fprintf(stderr, "citmail: could not connect to LMTP socket.\n"); cleanup(1); } sp = strchr (buf, ' '); if (sp == NULL) { if (debug) fprintf(stderr, "citmail: could not calculate hostname.\n"); cleanup(2); } sp ++; ep = strchr (sp, ' '); if (ep == NULL) { if (debug) fprintf(stderr, "citmail: error parsing hostname\n"); cleanup(3); } else *ep = '\0'; strncpy(hostname, sp, sizeof hostname); snprintf(fromline, sizeof fromline, "From: %s@%s", pw->pw_name, hostname); while (fgets(buf, 1024, stdin) != NULL) { if ( ( (buf[0] == 13) || (buf[0] == 10)) && (in_body == 0) ) { in_body = 1; if (from_header == 0) { fprintf(fp, "%s%s", fromline, buf); } } if (in_body == 0 && !strncasecmp(buf, "From:", 5)) { strcpy(fromline, buf); from_header = 1; } if (read_recipients_from_headers) { add_these_recipients = NULL; if ((isspace(buf[0])) && (to_or_cc)) { add_these_recipients = buf; } else { if ((!strncasecmp(buf, "To:", 3)) || (!strncasecmp(buf, "Cc:", 3))) { to_or_cc = 1; } else { to_or_cc = 0; } if (to_or_cc) { add_these_recipients = &buf[3]; } } if (add_these_recipients) { int num_recp_on_this_line; char this_recp[256]; num_recp_on_this_line = num_tokens(add_these_recipients, ','); for (i=0; i<num_recp_on_this_line; ++i) { extract_token(this_recp, add_these_recipients, i, ',', sizeof this_recp); striplt(this_recp); if (!IsEmptyStr(this_recp)) { ++num_recipients; recipients = realloc(recipients, (num_recipients * sizeof (char *))); recipients[num_recipients - 1] = strdup(this_recp); } } } } fprintf(fp, "%s", buf); } strip_trailing_nonprint(fromline); sprintf(buf, "LHLO %s", hostname); serv_puts(buf); do { serv_gets(buf); strcat(buf, " "); } while (buf[3] == '-'); if (buf[0] != '2') { if (debug) fprintf(stderr, "citmail: LHLO command failed\n"); cleanup(4); } snprintf(buf, sizeof buf, "MAIL %s", fromline); serv_puts(buf); serv_gets(buf); if (buf[0] != '2') { if (debug) fprintf(stderr, "citmail: MAIL command failed\n"); cleanup(5); } for (i=0; i<num_recipients; ++i) { snprintf(buf, sizeof buf, "RCPT To: %s", recipients[i]); serv_puts(buf); serv_gets(buf); free(recipients[i]); } free(recipients); serv_puts("DATA"); serv_gets(buf); if (buf[0]!='3') { if (debug) fprintf(stderr, "citmail: DATA command failed\n"); cleanup(6); } rewind(fp); while (fgets(buf, sizeof buf, fp) != NULL) { strip_trailing_nonprint(buf); serv_puts(buf); } serv_puts("."); serv_gets(buf); if (buf[0] != '2') { fprintf(stderr, "%s\n", &buf[4]); cleanup(7); } else { cleanup(0); } /* We won't actually reach this statement but the compiler will * display a spurious warning about an invalid return type if * we don't return an int. */ return(0); }
/* * Fetch a message from the server and extract a vNote from it */ struct vnote *vnote_new_from_msg(long msgnum,int unread) { StrBuf *Buf; StrBuf *Data = NULL; const char *bptr; int Done = 0; char uid_from_headers[256]; char mime_partnum[256]; char mime_filename[256]; char mime_content_type[256]; char mime_disposition[256]; char relevant_partnum[256]; int phase = 0; /* 0 = citadel headers, 1 = mime headers, 2 = body */ char msg4_content_type[256] = ""; char msg4_content_encoding[256] = ""; int msg4_content_length = 0; struct vnote *vnote_from_body = NULL; int vnote_inline = 0; /* 1 = MSG4 gave us a text/x-vnote top level */ relevant_partnum[0] = '\0'; serv_printf("MSG4 %ld", msgnum); /* we need the mime headers */ Buf = NewStrBuf(); StrBuf_ServGetln(Buf); if (GetServerStatus(Buf, NULL) != 1) { FreeStrBuf (&Buf); return NULL; } while ((StrBuf_ServGetln(Buf)>=0) && !Done) { if ( (StrLength(Buf)==3) && !strcmp(ChrPtr(Buf), "000")) { Done = 1; break; } bptr = ChrPtr(Buf); switch (phase) { case 0: if (!strncasecmp(bptr, "exti=", 5)) { safestrncpy(uid_from_headers, &(ChrPtr(Buf)[5]), sizeof uid_from_headers); } else if (!strncasecmp(bptr, "part=", 5)) { extract_token(mime_filename, &bptr[5], 1, '|', sizeof mime_filename); extract_token(mime_partnum, &bptr[5], 2, '|', sizeof mime_partnum); extract_token(mime_disposition, &bptr[5], 3, '|', sizeof mime_disposition); extract_token(mime_content_type, &bptr[5], 4, '|', sizeof mime_content_type); if (!strcasecmp(mime_content_type, "text/vnote")) { strcpy(relevant_partnum, mime_partnum); } } else if ((phase == 0) && (!strncasecmp(bptr, "text", 4))) { phase = 1; } break; case 1: if (!IsEmptyStr(bptr)) { if (!strncasecmp(bptr, "Content-type: ", 14)) { safestrncpy(msg4_content_type, &bptr[14], sizeof msg4_content_type); striplt(msg4_content_type); } else if (!strncasecmp(bptr, "Content-transfer-encoding: ", 27)) { safestrncpy(msg4_content_encoding, &bptr[27], sizeof msg4_content_encoding); striplt(msg4_content_type); } else if ((!strncasecmp(bptr, "Content-length: ", 16))) { msg4_content_length = atoi(&bptr[16]); } break; } else { phase++; if ((msg4_content_length > 0) && ( !strcasecmp(msg4_content_encoding, "7bit")) && (!strcasecmp(msg4_content_type, "text/vnote")) ) { vnote_inline = 1; } } case 2: if (vnote_inline) { Data = NewStrBufPlain(NULL, msg4_content_length * 2); if (msg4_content_length > 0) { StrBuf_ServGetBLOBBuffered(Data, msg4_content_length); phase ++; } else { StrBufAppendBuf(Data, Buf, 0); StrBufAppendBufPlain(Data, "\r\n", 1, 0); } } case 3: if (vnote_inline) { StrBufAppendBuf(Data, Buf, 0); } } } FreeStrBuf(&Buf); /* If MSG4 didn't give us the part we wanted, but we know that we can find it * as one of the other MIME parts, attempt to load it now. */ if ((!vnote_inline) && (!IsEmptyStr(relevant_partnum))) { Data = load_mimepart(msgnum, relevant_partnum); } if (StrLength(Data) > 0) { if (IsEmptyStr(uid_from_headers)) { /* Convert an old-style note to a vNote */ vnote_from_body = vnote_new(); vnote_from_body->uid = strdup(uid_from_headers); vnote_from_body->color_red = pastel_palette[3][0]; vnote_from_body->color_green = pastel_palette[3][1]; vnote_from_body->color_blue = pastel_palette[3][2]; vnote_from_body->body = malloc(StrLength(Data) + 1); vnote_from_body->body[0] = 0; memcpy(vnote_from_body->body, ChrPtr(Data), StrLength(Data) + 1); FreeStrBuf(&Data); return vnote_from_body; } else { char *Buf = SmashStrBuf(&Data); struct vnote *v = vnote_new_from_str(Buf); free(Buf); return(v); } } return NULL; }
static long parse_MimeHeaders(interesting_mime_headers *m, char** pcontent_start, char *content_end) { char buf[SIZ]; char header[SIZ]; long headerlen; char *ptr, *pch; int buflen = 0; int i; /* Learn interesting things from the headers */ ptr = *pcontent_start; *header = '\0'; headerlen = 0; do { ptr = memreadlinelen(ptr, buf, SIZ, &buflen); for (i = 0; i < buflen; ++i) { if (isspace(buf[i])) { buf[i] = ' '; } } if (!isspace(buf[0]) && (headerlen > 0)) { if (!strncasecmp(header, "Content-type:", 13)) { memcpy (m->b[content_type].Key, &header[13], headerlen - 12); m->b[content_type].Key[headerlen - 12] = '\0'; m->b[content_type].len = striplt (m->b[content_type].Key); m->b[content_type_name].len = extract_key(m->b[content_type_name].Key, CKEY(m->b[content_type]), HKEY("name"), '='); m->b[charset].len = extract_key(m->b[charset].Key, CKEY(m->b[content_type]), HKEY("charset"), '='); m->b[boundary].len = extract_key(m->b[boundary].Key, header, headerlen, HKEY("boundary"), '='); /* Deal with weird headers */ pch = strchr(m->b[content_type].Key, ' '); if (pch != NULL) { *pch = '\0'; m->b[content_type].len = m->b[content_type].Key - pch; } pch = strchr(m->b[content_type].Key, ';'); if (pch != NULL) { *pch = '\0'; m->b[content_type].len = m->b[content_type].Key - pch; } } else if (!strncasecmp(header, "Content-Disposition:", 20)) { memcpy (m->b[disposition].Key, &header[20], headerlen - 19); m->b[disposition].Key[headerlen - 19] = '\0'; m->b[disposition].len = striplt(m->b[disposition].Key); m->b[content_disposition_name].len = extract_key(m->b[content_disposition_name].Key, CKEY(m->b[disposition]), HKEY("name"), '='); m->b[filename].len = extract_key(m->b[filename].Key, CKEY(m->b[disposition]), HKEY("filename"), '='); pch = strchr(m->b[disposition].Key, ';'); if (pch != NULL) *pch = '\0'; m->b[disposition].len = striplt(m->b[disposition].Key); } else if (!strncasecmp(header, "Content-ID:", 11)) { memcpy(m->b[id].Key, &header[11], headerlen - 11); m->b[id].Key[headerlen - 11] = '\0'; striplt(m->b[id].Key); m->b[id].len = stripallbut(m->b[id].Key, '<', '>'); } else if (!strncasecmp(header, "Content-length: ", 15)) { char *clbuf; clbuf = &header[15]; while (isspace(*clbuf)) clbuf ++; m->content_length = (size_t) atol(clbuf); } else if (!strncasecmp(header, "Content-transfer-encoding: ", 26)) { memcpy(m->b[encoding].Key, &header[26], headerlen - 26); m->b[encoding].Key[headerlen - 26] = '\0'; m->b[encoding].len = striplt(m->b[encoding].Key); } *header = '\0'; headerlen = 0; } if ((headerlen + buflen + 2) < SIZ) { memcpy(&header[headerlen], buf, buflen); headerlen += buflen; header[headerlen] = '\0'; } if (ptr >= content_end) { return -1; } } while ((!IsEmptyStr(buf)) && (*ptr != 0)); m->is_multipart = m->b[boundary].len != 0; *pcontent_start = ptr; return 0; }
/* * Learn LDAP attributes and stuff them into the vCard. * Returns nonzero if we changed anything. */ int Ctdl_LDAP_to_vCard(char *ldap_dn, struct vCard *v) { int changed_something = 0; LDAP *ldserver = NULL; int i; struct timeval tv; LDAPMessage *search_result = NULL; LDAPMessage *entry = NULL; char **givenName; char **sn; char **cn; char **initials; char **o; char **street; char **l; char **st; char **postalCode; char **telephoneNumber; char **mobile; char **homePhone; char **facsimileTelephoneNumber; char **mail; char **uid; char **homeDirectory; char **uidNumber; char **loginShell; char **gidNumber; char **c; char **title; char **uuid; char *attrs[] = { "*","+",NULL}; if (!ldap_dn) return(0); if (!v) return(0); if (ctdl_ldap_initialize(&ldserver) != LDAP_SUCCESS) { return(0); } ldap_set_option(ldserver, LDAP_OPT_PROTOCOL_VERSION, &ctdl_require_ldap_version); ldap_set_option(ldserver, LDAP_OPT_REFERRALS, (void *)LDAP_OPT_OFF); striplt(config.c_ldap_bind_dn); striplt(config.c_ldap_bind_pw); syslog(LOG_DEBUG, "LDAP bind DN: %s", config.c_ldap_bind_dn); i = ldap_simple_bind_s(ldserver, (!IsEmptyStr(config.c_ldap_bind_dn) ? config.c_ldap_bind_dn : NULL), (!IsEmptyStr(config.c_ldap_bind_pw) ? config.c_ldap_bind_pw : NULL) ); if (i != LDAP_SUCCESS) { syslog(LOG_ALERT, "LDAP: Cannot bind: %s (%d)", ldap_err2string(i), i); return(0); } tv.tv_sec = 10; tv.tv_usec = 0; syslog(LOG_DEBUG, "LDAP search: %s", ldap_dn); (void) ldap_search_ext_s( ldserver, /* ld */ ldap_dn, /* base */ LDAP_SCOPE_SUBTREE, /* scope */ NULL, /* filter */ attrs, /* attrs (all attributes) */ 0, /* attrsonly (attrs + values) */ NULL, /* serverctrls (none) */ NULL, /* clientctrls (none) */ &tv, /* timeout */ 1, /* sizelimit (1 result max) */ &search_result /* res */ ); /* Ignore the return value of ldap_search_ext_s(). Sometimes it returns an error even when * the search succeeds. Instead, we check to see whether search_result is still NULL. */ if (search_result == NULL) { syslog(LOG_DEBUG, "LDAP search: zero results were returned"); ldap_unbind(ldserver); return(0); } /* At this point we've got at least one result from our query. If there are multiple * results, we still only look at the first one. */ entry = ldap_first_entry(ldserver, search_result); if (entry) { syslog(LOG_DEBUG, "LDAP search, got user details for vcard."); givenName=ldap_get_values(ldserver, search_result, "givenName"); sn=ldap_get_values(ldserver, search_result, "sn"); cn=ldap_get_values(ldserver, search_result, "cn"); initials=ldap_get_values(ldserver, search_result, "initials"); title=ldap_get_values(ldserver, search_result, "title"); o=ldap_get_values(ldserver, search_result, "o"); street=ldap_get_values(ldserver, search_result, "street"); l=ldap_get_values(ldserver, search_result, "l"); st=ldap_get_values(ldserver, search_result, "st"); postalCode=ldap_get_values(ldserver, search_result, "postalCode"); telephoneNumber=ldap_get_values(ldserver, search_result, "telephoneNumber"); mobile=ldap_get_values(ldserver, search_result, "mobile"); homePhone=ldap_get_values(ldserver, search_result, "homePhone"); facsimileTelephoneNumber=ldap_get_values(ldserver, search_result, "facsimileTelephoneNumber"); mail=ldap_get_values(ldserver, search_result, "mail"); uid=ldap_get_values(ldserver, search_result, "uid"); homeDirectory=ldap_get_values(ldserver, search_result, "homeDirectory"); uidNumber=ldap_get_values(ldserver, search_result, "uidNumber"); loginShell=ldap_get_values(ldserver, search_result, "loginShell"); gidNumber=ldap_get_values(ldserver, search_result, "gidNumber"); c=ldap_get_values(ldserver, search_result, "c"); uuid=ldap_get_values(ldserver, search_result, "entryUUID"); if (street && l && st && postalCode && c) changed_something |= vcard_set_one_prop_iff_different(v,"adr",";;%s;%s;%s;%s;%s",street[0],l[0],st[0],postalCode[0],c[0]); if (telephoneNumber) changed_something |= vcard_set_one_prop_iff_different(v,"tel;work","%s",telephoneNumber[0]); if (facsimileTelephoneNumber) changed_something |= vcard_set_one_prop_iff_different(v,"tel;fax","%s",facsimileTelephoneNumber[0]); if (mobile) changed_something |= vcard_set_one_prop_iff_different(v,"tel;cell","%s",mobile[0]); if (homePhone) changed_something |= vcard_set_one_prop_iff_different(v,"tel;home","%s",homePhone[0]); if (givenName && sn) { if (initials) { changed_something |= vcard_set_one_prop_iff_different(v,"n","%s;%s;%s",sn[0],givenName[0],initials[0]); } else { changed_something |= vcard_set_one_prop_iff_different(v,"n","%s;%s",sn[0],givenName[0]); } } if (mail) { changed_something |= vcard_set_props_iff_different(v,"email;internet",ldap_count_values(mail),mail); } if (uuid) changed_something |= vcard_set_one_prop_iff_different(v,"X-uuid","%s",uuid[0]); if (o) changed_something |= vcard_set_one_prop_iff_different(v,"org","%s",o[0]); if (cn) changed_something |= vcard_set_one_prop_iff_different(v,"fn","%s",cn[0]); if (title) changed_something |= vcard_set_one_prop_iff_different(v,"title","%s",title[0]); if (givenName) ldap_value_free(givenName); if (initials) ldap_value_free(initials); if (sn) ldap_value_free(sn); if (cn) ldap_value_free(cn); if (o) ldap_value_free(o); if (street) ldap_value_free(street); if (l) ldap_value_free(l); if (st) ldap_value_free(st); if (postalCode) ldap_value_free(postalCode); if (telephoneNumber) ldap_value_free(telephoneNumber); if (mobile) ldap_value_free(mobile); if (homePhone) ldap_value_free(homePhone); if (facsimileTelephoneNumber) ldap_value_free(facsimileTelephoneNumber); if (mail) ldap_value_free(mail); if (uid) ldap_value_free(uid); if (homeDirectory) ldap_value_free(homeDirectory); if (uidNumber) ldap_value_free(uidNumber); if (loginShell) ldap_value_free(loginShell); if (gidNumber) ldap_value_free(gidNumber); if (c) ldap_value_free(c); if (title) ldap_value_free(title); if (uuid) ldap_value_free(uuid); } /* free the results */ ldap_msgfree(search_result); /* unbind so we can go back in as the authenticating user */ ldap_unbind(ldserver); return(changed_something); /* tell the caller whether we made any changes */ }
/* * Split an RFC822-style address into userid, host, and full name * */ void process_rfc822_addr(const char *rfc822, char *user, char *node, char *name) { int a; strcpy(user, ""); strcpy(node, CtdlGetConfigStr("c_fqdn")); strcpy(name, ""); if (rfc822 == NULL) return; /* extract full name - first, it's From minus <userid> */ strcpy(name, rfc822); stripout(name, '<', '>'); /* strip anything to the left of a bang */ while ((!IsEmptyStr(name)) && (haschar(name, '!') > 0)) strcpy(name, &name[1]); /* and anything to the right of a @ or % */ for (a = 0; name[a] != '\0'; ++a) { if (name[a] == '@') { name[a] = 0; break; } if (name[a] == '%') { name[a] = 0; break; } } /* but if there are parentheses, that changes the rules... */ if ((haschar(rfc822, '(') == 1) && (haschar(rfc822, ')') == 1)) { strcpy(name, rfc822); stripallbut(name, '(', ')'); } /* but if there are a set of quotes, that supersedes everything */ if (haschar(rfc822, 34) == 2) { strcpy(name, rfc822); while ((!IsEmptyStr(name)) && (name[0] != 34)) { strcpy(&name[0], &name[1]); } strcpy(&name[0], &name[1]); for (a = 0; name[a] != '\0'; ++a) if (name[a] == 34) { name[a] = 0; break; } } /* extract user id */ strcpy(user, rfc822); /* first get rid of anything in parens */ stripout(user, '(', ')'); /* if there's a set of angle brackets, strip it down to that */ if ((haschar(user, '<') == 1) && (haschar(user, '>') == 1)) { stripallbut(user, '<', '>'); } /* strip anything to the left of a bang */ while ((!IsEmptyStr(user)) && (haschar(user, '!') > 0)) strcpy(user, &user[1]); /* and anything to the right of a @ or % */ for (a = 0; user[a] != '\0'; ++a) { if (user[a] == '@') { user[a] = 0; break; } if (user[a] == '%') { user[a] = 0; break; } } /* extract node name */ strcpy(node, rfc822); /* first get rid of anything in parens */ stripout(node, '(', ')'); /* if there's a set of angle brackets, strip it down to that */ if ((haschar(node, '<') == 1) && (haschar(node, '>') == 1)) { stripallbut(node, '<', '>'); } /* If no node specified, tack ours on instead */ if ( (haschar(node, '@')==0) && (haschar(node, '%')==0) && (haschar(node, '!')==0) ) { strcpy(node, CtdlGetConfigStr("c_nodename")); } else { /* strip anything to the left of a @ */ while ((!IsEmptyStr(node)) && (haschar(node, '@') > 0)) strcpy(node, &node[1]); /* strip anything to the left of a % */ while ((!IsEmptyStr(node)) && (haschar(node, '%') > 0)) strcpy(node, &node[1]); /* reduce multiple system bang paths to node!user */ while ((!IsEmptyStr(node)) && (haschar(node, '!') > 1)) strcpy(node, &node[1]); /* now get rid of the user portion of a node!user string */ for (a = 0; node[a] != '\0'; ++a) if (node[a] == '!') { node[a] = 0; break; } } /* strip leading and trailing spaces in all strings */ striplt(user); striplt(node); striplt(name); /* If we processed a string that had the address in angle brackets * but no name outside the brackets, we now have an empty name. In * this case, use the user portion of the address as the name. */ if ((IsEmptyStr(name)) && (!IsEmptyStr(user))) { strcpy(name, user); } }