aboutsummaryrefslogtreecommitdiffstats
path: root/src/libutil/str_util.c
diff options
context:
space:
mode:
authorVsevolod Stakhov <vsevolod@highsecure.ru>2015-09-08 13:41:48 +0100
committerVsevolod Stakhov <vsevolod@highsecure.ru>2015-09-08 13:41:48 +0100
commit58f7e9aa83e40fbfdc525760e935cde617aed28d (patch)
tree00b12096e6084e5c469bf174d574e384cc2ae5de /src/libutil/str_util.c
parentf4bcd9642cce794bf6beaaa19c0a6a6faaf2dbd5 (diff)
downloadrspamd-58f7e9aa83e40fbfdc525760e935cde617aed28d.tar.gz
rspamd-58f7e9aa83e40fbfdc525760e935cde617aed28d.zip
Add headers folding routine.
Diffstat (limited to 'src/libutil/str_util.c')
-rw-r--r--src/libutil/str_util.c164
1 files changed, 164 insertions, 0 deletions
diff --git a/src/libutil/str_util.c b/src/libutil/str_util.c
index 75a200db5..844962c25 100644
--- a/src/libutil/str_util.c
+++ b/src/libutil/str_util.c
@@ -863,3 +863,167 @@ rspamd_strings_levenshtein_distance (const gchar *s1, gsize s1len,
return column[s1len];
}
+
+GString *
+rspamd_header_value_fold (const gchar *name, const gchar *value)
+{
+ GString *res;
+ const guint fold_max = 76;
+ guint cur_len;
+ const gchar *p, *c;
+ gboolean first_token = TRUE;
+ enum {
+ fold_before,
+ fold_after
+ } fold_type;
+ enum {
+ read_token,
+ read_quoted,
+ after_quote,
+ fold_token,
+ } state = read_token, next_state = read_token;
+
+ g_assert (name != NULL);
+ g_assert (value != NULL);
+
+ res = g_string_sized_new (strlen (value));
+
+ c = value;
+ p = c;
+ /* name:<WSP> */
+ cur_len = strlen (name) + 2;
+
+ while (*p) {
+ switch (state) {
+ case read_token:
+ if (*p == ',' || *p == ';') {
+ /* We have something similar to the token's end, so check len */
+ if (cur_len > fold_max * 0.8 && cur_len < fold_max) {
+ /* We want fold */
+ fold_type = fold_after;
+ state = fold_token;
+ next_state = read_token;
+ }
+ else if (cur_len > fold_max && !first_token) {
+ fold_type = fold_before;
+ state = fold_token;
+ next_state = read_token;
+ }
+ else {
+ g_string_append_len (res, c, p - c);
+ c = p;
+ first_token = FALSE;
+ }
+ p ++;
+ }
+ else if (*p == '"') {
+ /* Fold before quoted tokens */
+ g_string_append_len (res, c, p - c);
+ c = p;
+ state = read_quoted;
+ }
+ else if (*p == '\r') {
+ /* Reset line length */
+ cur_len = 0;
+
+ while (g_ascii_isspace (*p)) {
+ p ++;
+ }
+
+ g_string_append_len (res, c, p - c);
+ c = p;
+ }
+ else if (g_ascii_isspace (*p)) {
+ if (cur_len > fold_max * 0.8 && cur_len < fold_max) {
+ /* We want fold */
+ fold_type = fold_after;
+ state = fold_token;
+ next_state = read_token;
+ }
+ else if (cur_len > fold_max && !first_token) {
+ fold_type = fold_before;
+ state = fold_token;
+ next_state = read_token;
+ }
+ else {
+ g_string_append_len (res, c, p - c);
+ c = p;
+ first_token = FALSE;
+ p ++;
+ }
+ }
+ else {
+ p ++;
+ cur_len ++;
+ }
+ break;
+ case fold_token:
+ /* Here, we have token start at 'c' and token end at 'p' */
+ if (fold_type == fold_after) {
+ g_string_append_len (res, c, p - c);
+ g_string_append_len (res, "\r\n\t", 3);
+
+ /* Skip space if needed */
+ if (g_ascii_isspace (*p)) {
+ p ++;
+ }
+ }
+ else {
+ /* Skip space if needed */
+ if (g_ascii_isspace (*c)) {
+ c ++;
+ }
+
+ g_string_append_len (res, "\r\n\t", 3);
+ g_string_append_len (res, c, p - c);
+ }
+
+ c = p;
+ state = next_state;
+ cur_len = 0;
+ first_token = TRUE;
+ break;
+
+ case read_quoted:
+ if (p != c && *p == '"') {
+ state = after_quote;
+ }
+ p ++;
+ cur_len ++;
+ break;
+
+ case after_quote:
+ state = read_token;
+ /* Skip one more character after the quote */
+ p ++;
+ cur_len ++;
+ g_string_append_len (res, c, p - c);
+ c = p;
+ first_token = TRUE;
+ break;
+ }
+ }
+
+ /* Last token */
+ switch (state) {
+ case read_token:
+ if (cur_len > fold_max && !first_token) {
+ g_string_append_len (res, "\r\n\t", 3);
+ g_string_append_len (res, c, p - c);
+ }
+ else {
+ g_string_append_len (res, c, p - c);
+ }
+ break;
+ case read_quoted:
+ case after_quote:
+ g_string_append_len (res, c, p - c);
+ break;
+
+ default:
+ g_assert (p == c);
+ break;
+ }
+
+ return res;
+}