diff --git a/doc/ChangeLog b/doc/ChangeLog index a4ce0045..30dfcedf 100755 --- a/doc/ChangeLog +++ b/doc/ChangeLog @@ -1,3 +1,24 @@ +Version 2.8.0, released August 2nd 2010 +======================================= + +- Truncate command line arguments if they exceed internal string lengths +- Changed encryption method to strong encryption, thanks to Lukasz Olejnik +- Implemented SLL passphrase, although this should be deprecated +- Fixed three XSS holes, thanks to Lukasz Olejnik +- Limit text body size for RSS feed to 2k to avoid lock-up for very long entries +- Fixed JS bug with "Fix text" +- Implemented 'Enable Smileys' +- Fixed problem with cloning and "Guest menu commands = .." being present in the config file +- Added option "Cell Style " +- Fixed bug with URL in expanding groups using top groups +- Fixed stack overflow of Select -> Edit with many replies +- Increased socket timeout, show content length error correctly +- Fixed bug with quick date filter and threaded display +- Fixed problem with "'" in french translation for JavaScript +- Added 'last 3 days' option from David Pilgram +- Do no thread evaluation when doing filtering +- Fixed usage of undefine 'date' under 'subst on reply...' + Version 2.7.8, released December 2nd 2009 ========================================= diff --git a/doc/index.html b/doc/index.html index d07300e7..e5727a27 100755 --- a/doc/index.html +++ b/doc/index.html @@ -28,7 +28,7 @@  [Links]  * 

Home of the Electronic Logbook package by Stefan Ritt

-
Current version is : 2.7.8
+
Current version is : 2.8.0
certificate diff --git a/doc/index_nd.html b/doc/index_nd.html index 2d1bc06f..7b0cd847 100644 --- a/doc/index_nd.html +++ b/doc/index_nd.html @@ -28,7 +28,7 @@  [Links]  * 

Home of the Electronic Logbook package by Stefan Ritt

-
Current version is : 2.7.8
+
Current version is : 2.8.0
certificate
diff --git a/resources/eloglang.brazilian b/resources/eloglang.brazilian index ed517189..dc1a161f 100755 --- a/resources/eloglang.brazilian +++ b/resources/eloglang.brazilian @@ -472,3 +472,5 @@ Invalid URL = Attribute "%s" is not allowed in config file = Only user %s can delete this entry = File system full, ELOG cannot continue to work = +3 Days = +Last 3 Days = diff --git a/resources/eloglang.bulgarian b/resources/eloglang.bulgarian index beae0f45..23aeb9d1 100755 --- a/resources/eloglang.bulgarian +++ b/resources/eloglang.bulgarian @@ -472,3 +472,5 @@ Invalid URL = Attribute "%s" is not allowed in config file = Only user %s can delete this entry = File system full, ELOG cannot continue to work = +3 Days = +Last 3 Days = diff --git a/resources/eloglang.czech b/resources/eloglang.czech index 6f53150f..8f45821f 100644 --- a/resources/eloglang.czech +++ b/resources/eloglang.czech @@ -469,3 +469,5 @@ Invalid URL = Attribute "%s" is not allowed in config file = Only user %s can delete this entry = File system full, ELOG cannot continue to work = +3 Days = +Last 3 Days = diff --git a/resources/eloglang.danish b/resources/eloglang.danish index 25a08276..94a8a8aa 100755 --- a/resources/eloglang.danish +++ b/resources/eloglang.danish @@ -468,3 +468,9 @@ Attribute "%s" is not allowed in config file = Attributten ''%s'' er ikke tillad Only user %s can delete this entry = Kun bruger %s kan slette dette indlæg File system full, ELOG cannot continue to work = Filsystemet er løbet fuld, ELOG kan ikke fortsætte + +# +#---- please translate following items and then remove this comment ----# +# +3 Days = +Last 3 Days = diff --git a/resources/eloglang.dutch b/resources/eloglang.dutch index 534c6059..5959a3ea 100755 --- a/resources/eloglang.dutch +++ b/resources/eloglang.dutch @@ -471,3 +471,5 @@ Only user %s can delete this entry = Alleen gebruiker %s kan dit b #---- please translate following items and then remove this comment ----# # File system full, ELOG cannot continue to work = +3 Days = +Last 3 Days = diff --git a/resources/eloglang.french b/resources/eloglang.french index ae0a96d4..200c98b2 100755 --- a/resources/eloglang.french +++ b/resources/eloglang.french @@ -465,3 +465,9 @@ Invalid URL = URL invalide Attribute "%s" is not allowed in config file = L'attribut "%s" n'est pas autorisé dans le fichier de configuration Only user %s can delete this entry = Seul l'utilisateur %s peut supprimer cette entrée File system full, ELOG cannot continue to work = Le système de fichiers est plein, ELOG ne peut plus fonctionner + +# +#---- please translate following items and then remove this comment ----# +# +3 Days = +Last 3 Days = diff --git a/resources/eloglang.german b/resources/eloglang.german index 986dc06d..ac6b0f04 100755 --- a/resources/eloglang.german +++ b/resources/eloglang.german @@ -464,4 +464,5 @@ Invalid URL = Ung Attribute "%s" is not allowed in config file = Das Attribut "%s" ist in der Konfigurationsdatei nicht erlaubt Only user %s can delete this entry = Nur der Benutzer %s kann diesen Entrag löschen File system full, ELOG cannot continue to work = Dateisystem voll, ELOG kann nicht weiterarbeiten - +3 Days = 3 Tage +Last 3 Days = Letzte 3 Tage diff --git a/resources/eloglang.italian b/resources/eloglang.italian index 0edc1e96..c4a3a511 100755 --- a/resources/eloglang.italian +++ b/resources/eloglang.italian @@ -467,3 +467,5 @@ Invalid URL = Attribute "%s" is not allowed in config file = Only user %s can delete this entry = File system full, ELOG cannot continue to work = +3 Days = +Last 3 Days = diff --git a/resources/eloglang.japanese b/resources/eloglang.japanese index 74d76c74..8bb6ec73 100755 --- a/resources/eloglang.japanese +++ b/resources/eloglang.japanese @@ -468,3 +468,5 @@ Invalid URL = Attribute "%s" is not allowed in config file = Only user %s can delete this entry = File system full, ELOG cannot continue to work = +3 Days = +Last 3 Days = diff --git a/resources/eloglang.ru_CP1251 b/resources/eloglang.ru_CP1251 index 3a9c3595..79a388dd 100644 --- a/resources/eloglang.ru_CP1251 +++ b/resources/eloglang.ru_CP1251 @@ -473,3 +473,5 @@ Invalid URL = Attribute "%s" is not allowed in config file = Only user %s can delete this entry = File system full, ELOG cannot continue to work = +3 Days = +Last 3 Days = diff --git a/resources/eloglang.spanish b/resources/eloglang.spanish index 7ca91cdf..386f57ce 100755 --- a/resources/eloglang.spanish +++ b/resources/eloglang.spanish @@ -474,3 +474,5 @@ Only user %s can delete this entry = S #---- please translate following items and then remove this comment ----# # File system full, ELOG cannot continue to work = +3 Days = +Last 3 Days = diff --git a/resources/eloglang.swedish b/resources/eloglang.swedish index d4ea75c3..8d5182c2 100644 --- a/resources/eloglang.swedish +++ b/resources/eloglang.swedish @@ -467,3 +467,9 @@ Invalid URL = Ogiltig URL Attribute "%s" is not allowed in config file = Attributet "%s" är inte tillåtet i konfigurationsfilen Only user %s can delete this entry = Endast användare %s kan radera denna post File system full, ELOG cannot continue to work = Filsystemet fullt, ELOG kan inte fortsätta arbeta + +# +#---- please translate following items and then remove this comment ----# +# +3 Days = +Last 3 Days = diff --git a/resources/eloglang.turkish b/resources/eloglang.turkish index 7629a30f..1ba493c5 100755 --- a/resources/eloglang.turkish +++ b/resources/eloglang.turkish @@ -471,3 +471,5 @@ Invalid URL = Attribute "%s" is not allowed in config file = Only user %s can delete this entry = File system full, ELOG cannot continue to work = +3 Days = +Last 3 Days = diff --git a/resources/eloglang.zh_CN-GB2312 b/resources/eloglang.zh_CN-GB2312 index 1b6f2ff8..4df91a7c 100644 --- a/resources/eloglang.zh_CN-GB2312 +++ b/resources/eloglang.zh_CN-GB2312 @@ -470,3 +470,5 @@ Invalid URL = Attribute "%s" is not allowed in config file = Only user %s can delete this entry = File system full, ELOG cannot continue to work = +3 Days = +Last 3 Days = diff --git a/resources/eloglang.zh_CN-UTF8 b/resources/eloglang.zh_CN-UTF8 index ca8e2c32..54660d48 100644 --- a/resources/eloglang.zh_CN-UTF8 +++ b/resources/eloglang.zh_CN-UTF8 @@ -470,3 +470,5 @@ Invalid URL = Attribute "%s" is not allowed in config file = Only user %s can delete this entry = File system full, ELOG cannot continue to work = +3 Days = +Last 3 Days = diff --git a/src/crypt.c b/src/crypt.c index b5e7761a..2885c1ba 100644 --- a/src/crypt.c +++ b/src/crypt.c @@ -30,13 +30,12 @@ typedef unsigned int uint32_t; #endif /* Structure to save state of computation between the single steps. */ -struct sha256_ctx -{ - uint32_t H[8]; +struct sha256_ctx { + uint32_t H[8]; - uint32_t total[2]; - uint32_t buflen; - char buffer[128]; /* NB: always correctly aligned for uint32_t. */ + uint32_t total[2]; + uint32_t buflen; + char buffer[128]; /* NB: always correctly aligned for uint32_t. */ }; @@ -50,58 +49,55 @@ struct sha256_ctx /* This array contains the bytes used to pad the buffer to the next 64-byte boundary. (FIPS 180-2:5.1.1) */ -static const unsigned char fillbuf[64] = { 0x80, 0 /* , 0, 0, ... */ }; +static const unsigned char fillbuf[64] = { 0x80, 0 /* , 0, 0, ... */ }; /* Constants for SHA256 from FIPS 180-2:4.2.2. */ -static const uint32_t K[64] = - { - 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, - 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, - 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, - 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, - 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, - 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, - 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, - 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, - 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, - 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, - 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, - 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, - 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, - 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, - 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, - 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 - }; +static const uint32_t K[64] = { + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, + 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, + 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, + 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, + 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, + 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, + 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, + 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, + 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, + 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, + 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, + 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, + 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, + 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 +}; /* Process LEN bytes of BUFFER, accumulating context into CTX. It is assumed that LEN % 64 == 0. */ -static void -sha256_process_block (const void *buffer, size_t len, struct sha256_ctx *ctx) +static void sha256_process_block(const void *buffer, size_t len, struct sha256_ctx *ctx) { - const uint32_t *words = buffer; - size_t nwords = len / sizeof (uint32_t); - uint32_t a = ctx->H[0]; - uint32_t b = ctx->H[1]; - uint32_t c = ctx->H[2]; - uint32_t d = ctx->H[3]; - uint32_t e = ctx->H[4]; - uint32_t f = ctx->H[5]; - uint32_t g = ctx->H[6]; - uint32_t h = ctx->H[7]; + const uint32_t *words = buffer; + size_t nwords = len / sizeof(uint32_t); + uint32_t a = ctx->H[0]; + uint32_t b = ctx->H[1]; + uint32_t c = ctx->H[2]; + uint32_t d = ctx->H[3]; + uint32_t e = ctx->H[4]; + uint32_t f = ctx->H[5]; + uint32_t g = ctx->H[6]; + uint32_t h = ctx->H[7]; - /* First increment the byte count. FIPS 180-2 specifies the possible - length of the file up to 2^64 bits. Here we only compute the - number of bytes. Do a double word increment. */ - ctx->total[0] += len; - if (ctx->total[0] < (int)len) - ++ctx->total[1]; + /* First increment the byte count. FIPS 180-2 specifies the possible + length of the file up to 2^64 bits. Here we only compute the + number of bytes. Do a double word increment. */ + ctx->total[0] += len; + if (ctx->total[0] < (int) len) + ++ctx->total[1]; - /* Process all bytes in the buffer with 64 bytes in each round of - the loop. */ - while (nwords > 0) - { + /* Process all bytes in the buffer with 64 bytes in each round of + the loop. */ + while (nwords > 0) { uint32_t W[64]; unsigned int t; uint32_t a_save = a; @@ -122,35 +118,33 @@ sha256_process_block (const void *buffer, size_t len, struct sha256_ctx *ctx) #define R1(x) (CYCLIC (x, 17) ^ CYCLIC (x, 19) ^ (x >> 10)) /* It is unfortunate that C does not provide an operator for - cyclic rotation. Hope the C compiler is smart enough. */ + cyclic rotation. Hope the C compiler is smart enough. */ #define CYCLIC(w, s) ((w >> s) | (w << (32 - s))) /* Compute the message schedule according to FIPS 180-2:6.2.2 step 2. */ - for (t = 0; t < 16; ++t) - { - W[t] = SWAP (*words); - ++words; - } + for (t = 0; t < 16; ++t) { + W[t] = SWAP(*words); + ++words; + } for (t = 16; t < 64; ++t) - W[t] = R1 (W[t - 2]) + W[t - 7] + R0 (W[t - 15]) + W[t - 16]; + W[t] = R1(W[t - 2]) + W[t - 7] + R0(W[t - 15]) + W[t - 16]; /* The actual computation according to FIPS 180-2:6.2.2 step 3. */ - for (t = 0; t < 64; ++t) - { - uint32_t T1 = h + S1 (e) + Ch (e, f, g) + K[t] + W[t]; - uint32_t T2 = S0 (a) + Maj (a, b, c); - h = g; - g = f; - f = e; - e = d + T1; - d = c; - c = b; - b = a; - a = T1 + T2; - } + for (t = 0; t < 64; ++t) { + uint32_t T1 = h + S1(e) + Ch(e, f, g) + K[t] + W[t]; + uint32_t T2 = S0(a) + Maj(a, b, c); + h = g; + g = f; + f = e; + e = d + T1; + d = c; + c = b; + b = a; + a = T1 + T2; + } /* Add the starting values of the context according to FIPS 180-2:6.2.2 - step 4. */ + step 4. */ a += a_save; b += b_save; c += c_save; @@ -162,36 +156,35 @@ sha256_process_block (const void *buffer, size_t len, struct sha256_ctx *ctx) /* Prepare for the next round. */ nwords -= 16; - } + } - /* Put checksum in context given as argument. */ - ctx->H[0] = a; - ctx->H[1] = b; - ctx->H[2] = c; - ctx->H[3] = d; - ctx->H[4] = e; - ctx->H[5] = f; - ctx->H[6] = g; - ctx->H[7] = h; + /* Put checksum in context given as argument. */ + ctx->H[0] = a; + ctx->H[1] = b; + ctx->H[2] = c; + ctx->H[3] = d; + ctx->H[4] = e; + ctx->H[5] = f; + ctx->H[6] = g; + ctx->H[7] = h; } /* Initialize structure containing state of computation. (FIPS 180-2:5.3.2) */ -static void -sha256_init_ctx (struct sha256_ctx *ctx) +static void sha256_init_ctx(struct sha256_ctx *ctx) { - ctx->H[0] = 0x6a09e667; - ctx->H[1] = 0xbb67ae85; - ctx->H[2] = 0x3c6ef372; - ctx->H[3] = 0xa54ff53a; - ctx->H[4] = 0x510e527f; - ctx->H[5] = 0x9b05688c; - ctx->H[6] = 0x1f83d9ab; - ctx->H[7] = 0x5be0cd19; + ctx->H[0] = 0x6a09e667; + ctx->H[1] = 0xbb67ae85; + ctx->H[2] = 0x3c6ef372; + ctx->H[3] = 0xa54ff53a; + ctx->H[4] = 0x510e527f; + ctx->H[5] = 0x9b05688c; + ctx->H[6] = 0x1f83d9ab; + ctx->H[7] = 0x5be0cd19; - ctx->total[0] = ctx->total[1] = 0; - ctx->buflen = 0; + ctx->total[0] = ctx->total[1] = 0; + ctx->buflen = 0; } @@ -200,68 +193,61 @@ sha256_init_ctx (struct sha256_ctx *ctx) IMPORTANT: On some systems it is required that RESBUF is correctly aligned for a 32 bits value. */ -static void * -sha256_finish_ctx (struct sha256_ctx *ctx, void *resbuf) +static void *sha256_finish_ctx(struct sha256_ctx *ctx, void *resbuf) { - /* Take yet unprocessed bytes into account. */ - uint32_t bytes = ctx->buflen; - size_t pad; - unsigned int i; + /* Take yet unprocessed bytes into account. */ + uint32_t bytes = ctx->buflen; + size_t pad; + unsigned int i; - /* Now count remaining bytes. */ - ctx->total[0] += bytes; - if (ctx->total[0] < bytes) - ++ctx->total[1]; + /* Now count remaining bytes. */ + ctx->total[0] += bytes; + if (ctx->total[0] < bytes) + ++ctx->total[1]; - pad = bytes >= 56 ? 64 + 56 - bytes : 56 - bytes; - memcpy (&ctx->buffer[bytes], fillbuf, pad); + pad = bytes >= 56 ? 64 + 56 - bytes : 56 - bytes; + memcpy(&ctx->buffer[bytes], fillbuf, pad); - /* Put the 64-bit file length in *bits* at the end of the buffer. */ - *(uint32_t *) &ctx->buffer[bytes + pad + 4] = SWAP (ctx->total[0] << 3); - *(uint32_t *) &ctx->buffer[bytes + pad] = SWAP ((ctx->total[1] << 3) | - (ctx->total[0] >> 29)); + /* Put the 64-bit file length in *bits* at the end of the buffer. */ + *(uint32_t *) & ctx->buffer[bytes + pad + 4] = SWAP(ctx->total[0] << 3); + *(uint32_t *) & ctx->buffer[bytes + pad] = SWAP((ctx->total[1] << 3) | (ctx->total[0] >> 29)); - /* Process last bytes. */ - sha256_process_block (ctx->buffer, bytes + pad + 8, ctx); + /* Process last bytes. */ + sha256_process_block(ctx->buffer, bytes + pad + 8, ctx); - /* Put result from CTX in first 32 bytes following RESBUF. */ - for (i = 0; i < 8; ++i) - ((uint32_t *) resbuf)[i] = SWAP (ctx->H[i]); + /* Put result from CTX in first 32 bytes following RESBUF. */ + for (i = 0; i < 8; ++i) + ((uint32_t *) resbuf)[i] = SWAP(ctx->H[i]); - return resbuf; + return resbuf; } -static void -sha256_process_bytes (const void *buffer, size_t len, struct sha256_ctx *ctx) +static void sha256_process_bytes(const void *buffer, size_t len, struct sha256_ctx *ctx) { - /* When we already have some bits in our internal buffer concatenate - both inputs first. */ - if (ctx->buflen != 0) - { + /* When we already have some bits in our internal buffer concatenate + both inputs first. */ + if (ctx->buflen != 0) { size_t left_over = ctx->buflen; size_t add = 128 - left_over > len ? len : 128 - left_over; - memcpy (&ctx->buffer[left_over], buffer, add); + memcpy(&ctx->buffer[left_over], buffer, add); ctx->buflen += add; - if (ctx->buflen > 64) - { - sha256_process_block (ctx->buffer, ctx->buflen & ~63, ctx); + if (ctx->buflen > 64) { + sha256_process_block(ctx->buffer, ctx->buflen & ~63, ctx); - ctx->buflen &= 63; - /* The regions in the following copy operation cannot overlap. */ - memcpy (ctx->buffer, &ctx->buffer[(left_over + add) & ~63], - ctx->buflen); - } + ctx->buflen &= 63; + /* The regions in the following copy operation cannot overlap. */ + memcpy(ctx->buffer, &ctx->buffer[(left_over + add) & ~63], ctx->buflen); + } buffer = (const char *) buffer + add; len -= add; - } + } - /* Process available complete blocks. */ - if (len >= 64) - { + /* Process available complete blocks. */ + if (len >= 64) { /* To check alignment gcc has an appropriate operator. Other compilers don't. */ #if __GNUC__ >= 2 @@ -269,36 +255,31 @@ sha256_process_bytes (const void *buffer, size_t len, struct sha256_ctx *ctx) #else # define UNALIGNED_P(p) (((uintptr_t) p) % sizeof (uint32_t) != 0) #endif - if (UNALIGNED_P (buffer)) - while (len > 64) - { - sha256_process_block (memcpy (ctx->buffer, buffer, 64), 64, ctx); - buffer = (const char *) buffer + 64; - len -= 64; - } - else - { - sha256_process_block (buffer, len & ~63, ctx); - buffer = (const char *) buffer + (len & ~63); - len &= 63; - } - } + if (UNALIGNED_P(buffer)) + while (len > 64) { + sha256_process_block(memcpy(ctx->buffer, buffer, 64), 64, ctx); + buffer = (const char *) buffer + 64; + len -= 64; + } else { + sha256_process_block(buffer, len & ~63, ctx); + buffer = (const char *) buffer + (len & ~63); + len &= 63; + } + } - /* Move remaining bytes into internal buffer. */ - if (len > 0) - { + /* Move remaining bytes into internal buffer. */ + if (len > 0) { size_t left_over = ctx->buflen; - memcpy (&ctx->buffer[left_over], buffer, len); + memcpy(&ctx->buffer[left_over], buffer, len); left_over += len; - if (left_over >= 64) - { - sha256_process_block (ctx->buffer, 64, ctx); - left_over -= 64; - memcpy (ctx->buffer, &ctx->buffer[64], left_over); - } + if (left_over >= 64) { + sha256_process_block(ctx->buffer, 64, ctx); + left_over -= 64; + memcpy(ctx->buffer, &ctx->buffer[64], left_over); + } ctx->buflen = left_over; - } + } } @@ -319,214 +300,200 @@ static const char sha256_rounds_prefix[] = "rounds="; #define ROUNDS_MAX 999999999 /* Table with characters for base64 transformation. */ -static const char b64t[64] = -"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; +static const char b64t[64] = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; -static char * -sha256_crypt_r (const char *key, const char *salt, char *buffer, int buflen) +static char *sha256_crypt_r(const char *key, const char *salt, char *buffer, int buflen) { #ifdef _MSC_VER - unsigned char alt_result[32]; - unsigned char temp_result[32]; + unsigned char alt_result[32]; + unsigned char temp_result[32]; #else unsigned char alt_result[32] - __attribute__ ((__aligned__ (__alignof__ (uint32_t)))); - unsigned char temp_result[32] - __attribute__ ((__aligned__ (__alignof__ (uint32_t)))); + __attribute__ ((__aligned__(__alignof__(uint32_t)))); + unsigned char temp_result[32] + __attribute__ ((__aligned__(__alignof__(uint32_t)))); #endif - struct sha256_ctx ctx; - struct sha256_ctx alt_ctx; - size_t salt_len; - size_t key_len; - size_t cnt; - char *cp; - char *copied_key = NULL; - char *copied_salt = NULL; - char *p_bytes; - char *s_bytes; - /* Default number of rounds. */ - size_t rounds = ROUNDS_DEFAULT; - int rounds_custom = 0; + struct sha256_ctx ctx; + struct sha256_ctx alt_ctx; + size_t salt_len; + size_t key_len; + size_t cnt; + char *cp; + char *copied_key = NULL; + char *copied_salt = NULL; + char *p_bytes; + char *s_bytes; + /* Default number of rounds. */ + size_t rounds = ROUNDS_DEFAULT; + int rounds_custom = 0; - /* Find beginning of salt string. The prefix should normally always - be present. Just in case it is not. */ - if (strncmp (sha256_salt_prefix, salt, sizeof (sha256_salt_prefix) - 1) == 0) - /* Skip salt prefix. */ - salt += sizeof (sha256_salt_prefix) - 1; + /* Find beginning of salt string. The prefix should normally always + be present. Just in case it is not. */ + if (strncmp(sha256_salt_prefix, salt, sizeof(sha256_salt_prefix) - 1) == 0) + /* Skip salt prefix. */ + salt += sizeof(sha256_salt_prefix) - 1; - if (strncmp (salt, sha256_rounds_prefix, sizeof (sha256_rounds_prefix) - 1) - == 0) - { - const char *num = salt + sizeof (sha256_rounds_prefix) - 1; + if (strncmp(salt, sha256_rounds_prefix, sizeof(sha256_rounds_prefix) - 1) + == 0) { + const char *num = salt + sizeof(sha256_rounds_prefix) - 1; char *endp; - unsigned long int srounds = strtoul (num, &endp, 10); - if (*endp == '$') - { - salt = endp + 1; - rounds = MAX (ROUNDS_MIN, MIN (srounds, ROUNDS_MAX)); - rounds_custom = 1; - } - } + unsigned long int srounds = strtoul(num, &endp, 10); + if (*endp == '$') { + salt = endp + 1; + rounds = MAX(ROUNDS_MIN, MIN(srounds, ROUNDS_MAX)); + rounds_custom = 1; + } + } - salt_len = MIN (strcspn (salt, "$"), SALT_LEN_MAX); - key_len = strlen (key); + salt_len = MIN(strcspn(salt, "$"), SALT_LEN_MAX); + key_len = strlen(key); - if ((key - (char *) 0) % __alignof__ (uint32_t) != 0) - { - char *tmp = (char *) alloca (key_len + __alignof__ (uint32_t)); - key = copied_key = - memcpy (tmp + __alignof__ (uint32_t) - - (tmp - (char *) 0) % __alignof__ (uint32_t), - key, key_len); - } + if ((key - (char *) 0) % __alignof__(uint32_t) != 0) { + char *tmp = (char *) alloca(key_len + __alignof__(uint32_t)); + key = copied_key = memcpy(tmp + __alignof__(uint32_t) + - (tmp - (char *) 0) % __alignof__(uint32_t), key, key_len); + } - if ((salt - (char *) 0) % __alignof__ (uint32_t) != 0) - { - char *tmp = (char *) alloca (salt_len + __alignof__ (uint32_t)); - salt = copied_salt = - memcpy (tmp + __alignof__ (uint32_t) - - (tmp - (char *) 0) % __alignof__ (uint32_t), - salt, salt_len); - } + if ((salt - (char *) 0) % __alignof__(uint32_t) != 0) { + char *tmp = (char *) alloca(salt_len + __alignof__(uint32_t)); + salt = copied_salt = memcpy(tmp + __alignof__(uint32_t) + - (tmp - (char *) 0) % __alignof__(uint32_t), salt, salt_len); + } - /* Prepare for the real work. */ - sha256_init_ctx (&ctx); + /* Prepare for the real work. */ + sha256_init_ctx(&ctx); - /* Add the key string. */ - sha256_process_bytes (key, key_len, &ctx); + /* Add the key string. */ + sha256_process_bytes(key, key_len, &ctx); - /* The last part is the salt string. This must be at most 16 - characters and it ends at the first `$' character (for - compatibility with existing implementations). */ - sha256_process_bytes (salt, salt_len, &ctx); + /* The last part is the salt string. This must be at most 16 + characters and it ends at the first `$' character (for + compatibility with existing implementations). */ + sha256_process_bytes(salt, salt_len, &ctx); - /* Compute alternate SHA256 sum with input KEY, SALT, and KEY. The - final result will be added to the first context. */ - sha256_init_ctx (&alt_ctx); + /* Compute alternate SHA256 sum with input KEY, SALT, and KEY. The + final result will be added to the first context. */ + sha256_init_ctx(&alt_ctx); - /* Add key. */ - sha256_process_bytes (key, key_len, &alt_ctx); + /* Add key. */ + sha256_process_bytes(key, key_len, &alt_ctx); - /* Add salt. */ - sha256_process_bytes (salt, salt_len, &alt_ctx); + /* Add salt. */ + sha256_process_bytes(salt, salt_len, &alt_ctx); - /* Add key again. */ - sha256_process_bytes (key, key_len, &alt_ctx); + /* Add key again. */ + sha256_process_bytes(key, key_len, &alt_ctx); - /* Now get result of this (32 bytes) and add it to the other - context. */ - sha256_finish_ctx (&alt_ctx, alt_result); + /* Now get result of this (32 bytes) and add it to the other + context. */ + sha256_finish_ctx(&alt_ctx, alt_result); - /* Add for any character in the key one byte of the alternate sum. */ - for (cnt = key_len; cnt > 32; cnt -= 32) - sha256_process_bytes (alt_result, 32, &ctx); - sha256_process_bytes (alt_result, cnt, &ctx); + /* Add for any character in the key one byte of the alternate sum. */ + for (cnt = key_len; cnt > 32; cnt -= 32) + sha256_process_bytes(alt_result, 32, &ctx); + sha256_process_bytes(alt_result, cnt, &ctx); - /* Take the binary representation of the length of the key and for every - 1 add the alternate sum, for every 0 the key. */ - for (cnt = key_len; cnt > 0; cnt >>= 1) - if ((cnt & 1) != 0) - sha256_process_bytes (alt_result, 32, &ctx); - else - sha256_process_bytes (key, key_len, &ctx); + /* Take the binary representation of the length of the key and for every + 1 add the alternate sum, for every 0 the key. */ + for (cnt = key_len; cnt > 0; cnt >>= 1) + if ((cnt & 1) != 0) + sha256_process_bytes(alt_result, 32, &ctx); + else + sha256_process_bytes(key, key_len, &ctx); - /* Create intermediate result. */ - sha256_finish_ctx (&ctx, alt_result); + /* Create intermediate result. */ + sha256_finish_ctx(&ctx, alt_result); - /* Start computation of P byte sequence. */ - sha256_init_ctx (&alt_ctx); + /* Start computation of P byte sequence. */ + sha256_init_ctx(&alt_ctx); - /* For every character in the password add the entire password. */ - for (cnt = 0; cnt < key_len; ++cnt) - sha256_process_bytes (key, key_len, &alt_ctx); + /* For every character in the password add the entire password. */ + for (cnt = 0; cnt < key_len; ++cnt) + sha256_process_bytes(key, key_len, &alt_ctx); - /* Finish the digest. */ - sha256_finish_ctx (&alt_ctx, temp_result); + /* Finish the digest. */ + sha256_finish_ctx(&alt_ctx, temp_result); - /* Create byte sequence P. */ - cp = p_bytes = alloca (key_len); - for (cnt = key_len; cnt >= 32; cnt -= 32) - cp = mempcpy (cp, temp_result, 32); - memcpy (cp, temp_result, cnt); + /* Create byte sequence P. */ + cp = p_bytes = alloca(key_len); + for (cnt = key_len; cnt >= 32; cnt -= 32) + cp = mempcpy(cp, temp_result, 32); + memcpy(cp, temp_result, cnt); - /* Start computation of S byte sequence. */ - sha256_init_ctx (&alt_ctx); + /* Start computation of S byte sequence. */ + sha256_init_ctx(&alt_ctx); - /* For every character in the password add the entire password. */ - for (cnt = 0; (int)cnt < 16 + alt_result[0]; ++cnt) - sha256_process_bytes (salt, salt_len, &alt_ctx); + /* For every character in the password add the entire password. */ + for (cnt = 0; (int) cnt < 16 + alt_result[0]; ++cnt) + sha256_process_bytes(salt, salt_len, &alt_ctx); - /* Finish the digest. */ - sha256_finish_ctx (&alt_ctx, temp_result); + /* Finish the digest. */ + sha256_finish_ctx(&alt_ctx, temp_result); - /* Create byte sequence S. */ - cp = s_bytes = alloca (salt_len); - for (cnt = salt_len; cnt >= 32; cnt -= 32) - cp = mempcpy (cp, temp_result, 32); - memcpy (cp, temp_result, cnt); + /* Create byte sequence S. */ + cp = s_bytes = alloca(salt_len); + for (cnt = salt_len; cnt >= 32; cnt -= 32) + cp = mempcpy(cp, temp_result, 32); + memcpy(cp, temp_result, cnt); - /* Repeatedly run the collected hash value through SHA256 to burn - CPU cycles. */ - for (cnt = 0; cnt < rounds; ++cnt) - { + /* Repeatedly run the collected hash value through SHA256 to burn + CPU cycles. */ + for (cnt = 0; cnt < rounds; ++cnt) { /* New context. */ - sha256_init_ctx (&ctx); + sha256_init_ctx(&ctx); /* Add key or last result. */ if ((cnt & 1) != 0) - sha256_process_bytes (p_bytes, key_len, &ctx); + sha256_process_bytes(p_bytes, key_len, &ctx); else - sha256_process_bytes (alt_result, 32, &ctx); + sha256_process_bytes(alt_result, 32, &ctx); /* Add salt for numbers not divisible by 3. */ if (cnt % 3 != 0) - sha256_process_bytes (s_bytes, salt_len, &ctx); + sha256_process_bytes(s_bytes, salt_len, &ctx); /* Add key for numbers not divisible by 7. */ if (cnt % 7 != 0) - sha256_process_bytes (p_bytes, key_len, &ctx); + sha256_process_bytes(p_bytes, key_len, &ctx); /* Add key or last result. */ if ((cnt & 1) != 0) - sha256_process_bytes (alt_result, 32, &ctx); + sha256_process_bytes(alt_result, 32, &ctx); else - sha256_process_bytes (p_bytes, key_len, &ctx); + sha256_process_bytes(p_bytes, key_len, &ctx); /* Create intermediate result. */ - sha256_finish_ctx (&ctx, alt_result); - } + sha256_finish_ctx(&ctx, alt_result); + } - /* Now we can construct the result string. It consists of three - parts. */ - strncpy (buffer, sha256_salt_prefix, MAX (0, buflen)); - cp = buffer + strlen(buffer); - buflen -= sizeof (sha256_salt_prefix) - 1; + /* Now we can construct the result string. It consists of three + parts. */ + strncpy(buffer, sha256_salt_prefix, MAX(0, buflen)); + cp = buffer + strlen(buffer); + buflen -= sizeof(sha256_salt_prefix) - 1; - if (rounds_custom) - { + if (rounds_custom) { #ifdef _MSC_VER - int n = _snprintf (cp, MAX (0, buflen), "%s%zu$", - sha256_rounds_prefix, rounds); + int n = _snprintf(cp, MAX(0, buflen), "%s%zu$", + sha256_rounds_prefix, rounds); #else - int n = snprintf (cp, MAX (0, buflen), "%s%zu$", - sha256_rounds_prefix, rounds); + int n = snprintf(cp, MAX(0, buflen), "%s%zu$", + sha256_rounds_prefix, rounds); #endif cp += n; buflen -= n; - } + } - strncpy (cp, salt, MIN ((size_t) MAX (0, buflen), salt_len)); - cp = cp + strlen(cp); - buflen -= MIN ((size_t) MAX (0, buflen), salt_len); + strncpy(cp, salt, MIN((size_t) MAX(0, buflen), salt_len)); + cp = cp + strlen(cp); + buflen -= MIN((size_t) MAX(0, buflen), salt_len); - if (buflen > 0) - { + if (buflen > 0) { *cp++ = '$'; --buflen; - } - + } #define b64_from_24bit(B2, B1, B0, N) \ do { \ unsigned int w = ((B2) << 16) | ((B1) << 8) | (B0); \ @@ -539,70 +506,64 @@ sha256_crypt_r (const char *key, const char *salt, char *buffer, int buflen) } \ } while (0) - b64_from_24bit (alt_result[0], alt_result[10], alt_result[20], 4); - b64_from_24bit (alt_result[21], alt_result[1], alt_result[11], 4); - b64_from_24bit (alt_result[12], alt_result[22], alt_result[2], 4); - b64_from_24bit (alt_result[3], alt_result[13], alt_result[23], 4); - b64_from_24bit (alt_result[24], alt_result[4], alt_result[14], 4); - b64_from_24bit (alt_result[15], alt_result[25], alt_result[5], 4); - b64_from_24bit (alt_result[6], alt_result[16], alt_result[26], 4); - b64_from_24bit (alt_result[27], alt_result[7], alt_result[17], 4); - b64_from_24bit (alt_result[18], alt_result[28], alt_result[8], 4); - b64_from_24bit (alt_result[9], alt_result[19], alt_result[29], 4); - b64_from_24bit (0, alt_result[31], alt_result[30], 3); - if (buflen <= 0) - { + b64_from_24bit(alt_result[0], alt_result[10], alt_result[20], 4); + b64_from_24bit(alt_result[21], alt_result[1], alt_result[11], 4); + b64_from_24bit(alt_result[12], alt_result[22], alt_result[2], 4); + b64_from_24bit(alt_result[3], alt_result[13], alt_result[23], 4); + b64_from_24bit(alt_result[24], alt_result[4], alt_result[14], 4); + b64_from_24bit(alt_result[15], alt_result[25], alt_result[5], 4); + b64_from_24bit(alt_result[6], alt_result[16], alt_result[26], 4); + b64_from_24bit(alt_result[27], alt_result[7], alt_result[17], 4); + b64_from_24bit(alt_result[18], alt_result[28], alt_result[8], 4); + b64_from_24bit(alt_result[9], alt_result[19], alt_result[29], 4); + b64_from_24bit(0, alt_result[31], alt_result[30], 3); + if (buflen <= 0) { errno = ERANGE; buffer = NULL; - } - else - *cp = '\0'; /* Terminate the string. */ + } else + *cp = '\0'; /* Terminate the string. */ - /* Clear the buffer for the intermediate result so that people - attaching to processes or reading core dumps cannot get any - information. We do it in this way to clear correct_words[] - inside the SHA256 implementation as well. */ - sha256_init_ctx (&ctx); - sha256_finish_ctx (&ctx, alt_result); - memset (temp_result, '\0', sizeof (temp_result)); - memset (p_bytes, '\0', key_len); - memset (s_bytes, '\0', salt_len); - memset (&ctx, '\0', sizeof (ctx)); - memset (&alt_ctx, '\0', sizeof (alt_ctx)); - if (copied_key != NULL) - memset (copied_key, '\0', key_len); - if (copied_salt != NULL) - memset (copied_salt, '\0', salt_len); + /* Clear the buffer for the intermediate result so that people + attaching to processes or reading core dumps cannot get any + information. We do it in this way to clear correct_words[] + inside the SHA256 implementation as well. */ + sha256_init_ctx(&ctx); + sha256_finish_ctx(&ctx, alt_result); + memset(temp_result, '\0', sizeof(temp_result)); + memset(p_bytes, '\0', key_len); + memset(s_bytes, '\0', salt_len); + memset(&ctx, '\0', sizeof(ctx)); + memset(&alt_ctx, '\0', sizeof(alt_ctx)); + if (copied_key != NULL) + memset(copied_key, '\0', key_len); + if (copied_salt != NULL) + memset(copied_salt, '\0', salt_len); - return buffer; + return buffer; } /* This entry point is equivalent to the `crypt' function in Unix libcs. */ -char * -sha256_crypt (const char *key, const char *salt) +char *sha256_crypt(const char *key, const char *salt) { - /* We don't want to have an arbitrary limit in the size of the - password. We can compute an upper bound for the size of the - result in advance and so we can prepare the buffer we pass to - `sha256_crypt_r'. */ - static char *buffer; - static int buflen; - int needed = (sizeof (sha256_salt_prefix) - 1 - + sizeof (sha256_rounds_prefix) + 9 + 1 - + strlen (salt) + 1 + 43 + 1); + /* We don't want to have an arbitrary limit in the size of the + password. We can compute an upper bound for the size of the + result in advance and so we can prepare the buffer we pass to + `sha256_crypt_r'. */ + static char *buffer; + static int buflen; + int needed = (sizeof(sha256_salt_prefix) - 1 + + sizeof(sha256_rounds_prefix) + 9 + 1 + strlen(salt) + 1 + 43 + 1); - if (buflen < needed) - { - char *new_buffer = (char *) realloc (buffer, needed); + if (buflen < needed) { + char *new_buffer = (char *) realloc(buffer, needed); if (new_buffer == NULL) - return NULL; + return NULL; buffer = new_buffer; buflen = needed; - } + } - return sha256_crypt_r (key, salt, buffer, buflen); + return sha256_crypt_r(key, salt, buffer, buflen); } - diff --git a/src/elog.c b/src/elog.c index e7f1a596..69200314 100755 --- a/src/elog.c +++ b/src/elog.c @@ -9,7 +9,7 @@ \********************************************************************/ -#define VERSION "2.7.8" +#define VERSION "2.8.0" char svn_revision[] = "$Id$"; /* ELOG identification */ @@ -91,7 +91,7 @@ void base64_encode(unsigned char *s, unsigned char *d, int size) d += 4; if (d - p >= size - 3) - return; + return; } *d = 0; while (pad--) @@ -455,7 +455,7 @@ INT retrieve_elog(char *host, int port, char *subdir, int ssl, char *experiment, sprintf(request + strlen(request), "Cookie: "); first = 0; - do_crypt(upwd, encrypted_passwd, sizeof(encrypted_passwd) ); + do_crypt(upwd, encrypted_passwd, sizeof(encrypted_passwd)); sprintf(request + strlen(request), "upwd=%s;", encrypted_passwd); } @@ -819,7 +819,8 @@ INT submit_elog(char *host, int port, int ssl, char *subdir, char *experiment, if (upwd[0]) { do_crypt(upwd, encrypted_passwd, sizeof(encrypted_passwd)); sprintf(content + strlen(content), - "%s\r\nContent-Disposition: form-data; name=\"upwd\"\r\n\r\n%s\r\n", boundary, encrypted_passwd); + "%s\r\nContent-Disposition: form-data; name=\"upwd\"\r\n\r\n%s\r\n", boundary, + encrypted_passwd); } if (experiment[0]) @@ -901,7 +902,7 @@ INT submit_elog(char *host, int port, int ssl, char *subdir, char *experiment, sprintf(request + strlen(request), "Content-Length: %d\r\n", content_length); if (passwd[0]) { - do_crypt(passwd, encrypted_passwd, sizeof(encrypted_passwd) ); + do_crypt(passwd, encrypted_passwd, sizeof(encrypted_passwd)); sprintf(request + strlen(request), "Cookie: wpwd=%s\r\n", encrypted_passwd); } diff --git a/src/elogd.c b/src/elogd.c index e08ad582..92872186 100755 --- a/src/elogd.c +++ b/src/elogd.c @@ -9,7 +9,7 @@ \********************************************************************/ /* Version of ELOG */ -#define VERSION "2.7.8" +#define VERSION "2.8.0" char svn_revision[] = "$Id$"; /* ELOG identification */ @@ -1597,11 +1597,11 @@ void base64_bufenc(unsigned char *s, int len, char *d) *(--d) = '='; } -char *sha256_crypt (const char *key, const char *salt); +char *sha256_crypt(const char *key, const char *salt); void do_crypt(char *s, char *d, int size) { - strlcpy(d, sha256_crypt(s, "$5$")+4, size); + strlcpy(d, sha256_crypt(s, "$5$") + 4, size); } /*------------------------------------------------------------------* @@ -6030,7 +6030,7 @@ char void rsputs_elcode(LOGBOOK * lbs, BOOL email_notify, const char *str) { int i, j, k, l, m, elcode_disabled, elcode_disabled1, escape_char, ordered_list, substituted, inside_table, - smileys_enabled; + smileys_enabled; char *p, *pd, link[1000], link_text[1000], tmp[1000], attrib[1000], hattrib[1000], value[1000], subst[1000], base_url[256], param[256], *lstr, domain[256]; @@ -6190,7 +6190,7 @@ void rsputs_elcode(LOGBOOK * lbs, BOOL email_notify, const char *str) } /* check for blank before smiley and if smileys are allowed */ - if (l <= 20 && + if (l <= 20 && ((str[i - 1] != ' ' && str[i - 1] != '\r' && str[i - 1] != '\n') || (smileys_enabled == FALSE))) { strncpy(return_buffer + j, str + i, strlen(pattern_list[l].pattern)); @@ -9134,7 +9134,7 @@ void compare_attributes(LOGBOOK * lbs, int message_id, char attrib[MAX_N_ATTR][N memcpy(attrib, attr, sizeof(attr)); else { for (i = 0; i < lbs->n_attr; i++) - if (!strieq(attrib[i], attr+i*NAME_LENGTH)) + if (!strieq(attrib[i], attr + i * NAME_LENGTH)) sprintf(attrib[i], "- %s -", loc("keep original values")); } (*n)++; @@ -9146,9 +9146,9 @@ void compare_attributes(LOGBOOK * lbs, int message_id, char attrib[MAX_N_ATTR][N // go through all replies in threaded mode n_reply = strbreak(reply_to, (char (*)[NAME_LENGTH]) list, MAX_N_ATTR, ",", FALSE); for (i = 0; i < n_reply; i++) { - compare_attributes(lbs, atoi(list+i*NAME_LENGTH), attrib, n); + compare_attributes(lbs, atoi(list + i * NAME_LENGTH), attrib, n); - xfree(list); + xfree(list); } } @@ -16773,8 +16773,8 @@ void display_line(LOGBOOK * lbs, int message_id, int number, char *mode, int exp int *n_display, char *locked_by, int highlight, regex_t * re_buf, int highlight_mid, int absolute_link) { - char str[NAME_LENGTH], ref[256], *nowrap, rowstyle[80], tdstyle[80], format[256], file_name[MAX_PATH_LENGTH], *slist, - *svalue, comment[256], param[80]; + char str[NAME_LENGTH], ref[256], *nowrap, rowstyle[80], tdstyle[80], format[256], + file_name[MAX_PATH_LENGTH], *slist, *svalue, comment[256], param[80]; char display[NAME_LENGTH], attr_icon[80]; int i, j, n, i_line, index, colspan, n_attachments, line_len, thumb_status, max_line_len, n_lines, max_n_lines; @@ -17060,7 +17060,7 @@ void display_line(LOGBOOK * lbs, int message_id, int number, char *mode, int exp for (i = 0; i < n_attr; i++) if (strieq(disp_attr[index], attr_list[i])) { - + /* check attributes for cell style */ strlcpy(tdstyle, rowstyle, sizeof(tdstyle)); sprintf(str, "Cell Style %s %s", attr_list[i], attrib[i]); @@ -17068,7 +17068,7 @@ void display_line(LOGBOOK * lbs, int message_id, int number, char *mode, int exp sprintf(str, "%s\" style=\"%s\"", rowstyle, display); strlcpy(tdstyle, str, sizeof(rowstyle)); } - + if (strieq(mode, "Threaded")) { if (strieq(attr_options[i][0], "boolean")) { if (atoi(attrib[i]) == 1) { @@ -18277,7 +18277,8 @@ void show_page_filters(LOGBOOK * lbs, int n_msg, int page_n, BOOL mode_commands, rsprintf("