1 # include <stdio.h> 2 # include <ctype.h> 3 # include "dlvrmail.h" 4 5 static char SccsId[] = "@(#)parseaddr.c 1.3 08/02/80"; 6 7 /* 8 ** PARSE -- Parse an address 9 ** 10 ** Parses an address and breaks it up into three parts: a 11 ** net to transmit the message on, the host to transmit it 12 ** to, and a user on that host. These are loaded into an 13 ** addrq header with the values squirreled away if necessary. 14 ** The "user" part may not be a real user; the process may 15 ** just reoccur on that machine. For example, on a machine 16 ** with an arpanet connection, the address 17 ** csvax.bill@berkeley 18 ** will break up to a "user" of 'csvax.bill' and a host 19 ** of 'berkeley' -- to be transmitted over the arpanet. 20 ** 21 ** Parameters: 22 ** addr -- the address to parse. 23 ** a -- a pointer to the address descriptor buffer. 24 ** If NULL, a header will be created. 25 ** copyf -- determines what shall be copied: 26 ** -1 -- don't copy anything. The printname 27 ** (q_paddr) is just addr, and the 28 ** user & host are allocated internally 29 ** to parse. 30 ** 0 -- copy out the parsed user & host, but 31 ** don't copy the printname. 32 ** +1 -- copy everything. 33 ** 34 ** Returns: 35 ** A pointer to the address descriptor header (`a' if 36 ** `a' is non-NULL). 37 ** NULL on error. 38 ** 39 ** Side Effects: 40 ** none 41 ** 42 ** Called By: 43 ** main 44 ** sendto 45 ** alias 46 ** savemail 47 */ 48 49 addrq * 50 parse(addr, a, copyf) 51 char *addr; 52 register addrq *a; 53 int copyf; 54 { 55 register char *p; 56 register struct parsetab *t; 57 extern struct parsetab ParseTab[]; 58 static char buf[MAXNAME]; 59 register char c; 60 register char *q; 61 bool got_one; 62 extern char *prescan(); 63 extern char *xalloc(); 64 65 /* 66 ** Initialize and prescan address. 67 */ 68 69 To = addr; 70 if (prescan(addr, buf, &buf[sizeof buf], '\0') == NULL) 71 return (NULL); 72 73 /* 74 ** Scan parse table. 75 ** Look for the first entry designating a character 76 ** that is contained in the address. 77 ** Arrange for q to point to that character. 78 ** Check to see that there is only one of the char 79 ** if it must be unique. 80 ** Find the last one if the host is on the RHS. 81 ** Insist that the host name is atomic. 82 ** If just doing a map, do the map and then start all 83 ** over. 84 */ 85 86 rescan: 87 got_one = FALSE; 88 for (t = ParseTab; t->p_char != '\0'; t++) 89 { 90 q = NULL; 91 for (p = buf; (c = *p) != '\0'; p++) 92 { 93 /* find the end of this token */ 94 while (isalnum(c) || c == '-' || c == '_') 95 c = *++p; 96 if (c == '\0') 97 break; 98 99 if (c == t->p_char) 100 { 101 got_one = TRUE; 102 103 /* do mapping as appropriate */ 104 if (flagset(P_MAP, t->p_flags)) 105 { 106 *p = t->p_arg[0]; 107 if (flagset(P_ONE, t->p_flags)) 108 goto rescan; 109 else 110 continue; 111 } 112 113 /* arrange for q to point to it */ 114 if (q != NULL && flagset(P_ONE, t->p_flags)) 115 { 116 usrerr("multichar error"); 117 ExitStat = EX_USAGE; 118 return (NULL); 119 } 120 if (q == NULL || flagset(P_HLAST, t->p_flags)) 121 q = p; 122 } 123 else 124 { 125 /* insist that host name is atomic */ 126 if (flagset(P_HLAST, t->p_flags)) 127 q = NULL; 128 else 129 break; 130 } 131 } 132 133 if (q != NULL) 134 break; 135 } 136 137 /* 138 ** If we matched nothing cleanly, but we did match something 139 ** somewhere in the process of scanning, then we have a 140 ** syntax error. This can happen on things like a@b:c where 141 ** @ has a right host and : has a left host. 142 ** 143 ** We also set `q' to the null string, in case someone forgets 144 ** to put the P_MOVE bit in the local mailer entry of the 145 ** configuration table. 146 */ 147 148 if (q == NULL) 149 { 150 q = ""; 151 if (got_one) 152 { 153 usrerr("syntax error"); 154 ExitStat = EX_USAGE; 155 return (NULL); 156 } 157 } 158 159 /* 160 ** Interpret entry. 161 ** t points to the entry for the mailer we will use. 162 ** q points to the significant character. 163 */ 164 165 if (a == NULL) 166 a = (addrq *) xalloc(sizeof *a); 167 if (copyf > 0) 168 { 169 p = xalloc((unsigned) strlen(addr) + 1); 170 strcpy(p, addr); 171 a->q_paddr = p; 172 } 173 else 174 a->q_paddr = addr; 175 a->q_mailer = &Mailer[t->p_mailer]; 176 177 if (flagset(P_MOVE, t->p_flags)) 178 { 179 /* send the message to another host & retry */ 180 a->q_host = t->p_arg; 181 if (copyf >= 0) 182 { 183 p = xalloc((unsigned) strlen(buf) + 1); 184 strcpy(p, buf); 185 a->q_user = p; 186 } 187 else 188 a->q_user = buf; 189 } 190 else 191 { 192 /* 193 ** Make local copies of the host & user and then 194 ** transport them out. 195 */ 196 197 *q++ = '\0'; 198 if (flagset(P_HLAST, t->p_flags)) 199 { 200 a->q_host = q; 201 a->q_user = buf; 202 } 203 else 204 { 205 a->q_host = buf; 206 a->q_user = q; 207 } 208 if (copyf >= 0) 209 { 210 p = xalloc((unsigned) strlen(a->q_host) + 1); 211 strcpy(p, a->q_host); 212 a->q_host = p; 213 p = xalloc((unsigned) strlen(a->q_user) + 1); 214 strcpy(p, a->q_user); 215 a->q_user = p; 216 } 217 } 218 219 /* 220 ** Do UPPER->lower case mapping unless inhibited. 221 */ 222 223 if (!flagset(P_HST_UPPER, t->p_flags)) 224 makelower(a->q_host); 225 if (!flagset(P_USR_UPPER, t->p_flags)) 226 makelower(a->q_user); 227 228 /* 229 ** Compute return value. 230 */ 231 232 # ifdef DEBUG 233 if (Debug && copyf >= 0) 234 printf("parse(\"%s\"): host \"%s\" user \"%s\" mailer %d\n", 235 addr, a->q_host, a->q_user, t->p_mailer); 236 # endif DEBUG 237 238 return (a); 239 } 240 /* 241 ** MAKELOWER -- Translate a line into lower case 242 ** 243 ** Parameters: 244 ** p -- the string to translate. If NULL, return is 245 ** immediate. 246 ** 247 ** Returns: 248 ** none. 249 ** 250 ** Side Effects: 251 ** String pointed to by p is translated to lower case. 252 ** 253 ** Called By: 254 ** parse 255 */ 256 257 makelower(p) 258 register char *p; 259 { 260 register char c; 261 262 if (p == NULL) 263 return; 264 for (; (c = *p) != '\0'; p++) 265 if ((c & 0200) == 0 && isupper(c)) 266 *p = c - 'A' + 'a'; 267 } 268 /* 269 ** PRESCAN -- Prescan name and make it canonical 270 ** 271 ** Scans a name and turns it into canonical form. This involves 272 ** deleting blanks, comments (in parentheses), and turning the 273 ** word "at" into an at-sign ("@"). The name is copied as this 274 ** is done; it is legal to copy a name onto itself, since this 275 ** process can only make things smaller. 276 ** 277 ** This routine knows about quoted strings and angle brackets. 278 ** 279 ** There are certain subtleties to this routine. The one that 280 ** comes to mind now is that backslashes on the ends of names 281 ** are silently stripped off; this is intentional. The problem 282 ** is that some versions of sndmsg (like at LBL) set the kill 283 ** character to something other than @ when reading addresses; 284 ** so people type "csvax.eric\@berkeley" -- which screws up the 285 ** berknet mailer. 286 ** 287 ** Parameters: 288 ** addr -- the name to chomp. 289 ** buf -- the buffer to copy it into. 290 ** buflim -- the last usable address in the buffer 291 ** (which will old a null byte). Normally 292 ** &buf[sizeof buf - 1]. 293 ** delim -- the delimiter for the address, normally 294 ** '\0' or ','; \0 is accepted in any case. 295 ** are moving in place; set buflim to high core. 296 ** 297 ** Returns: 298 ** A pointer to the terminator of buf. 299 ** NULL on error. 300 ** 301 ** Side Effects: 302 ** buf gets clobbered. 303 ** 304 ** Called By: 305 ** parse 306 ** maketemp 307 */ 308 309 char * 310 prescan(addr, buf, buflim, delim) 311 char *addr; 312 char *buf; 313 char *buflim; 314 char delim; 315 { 316 register char *p; 317 bool space; 318 bool quotemode; 319 bool bslashmode; 320 int cmntcnt; 321 int brccnt; 322 register char c; 323 register char *q; 324 extern bool any(); 325 326 space = TRUE; 327 q = buf; 328 bslashmode = quotemode = FALSE; 329 cmntcnt = brccnt = 0; 330 for (p = addr; (c = *p++ & 0177) != '\0'; ) 331 { 332 /* chew up special characters */ 333 *q = '\0'; 334 if (bslashmode) 335 { 336 c |= 0200; 337 bslashmode == FALSE; 338 } 339 else if (c == '"') 340 quotemode = !quotemode; 341 else if (c == '\\') 342 { 343 bslashmode++; 344 continue; 345 } 346 else if (quotemode) 347 c |= 0200; 348 else if (c == delim) 349 break; 350 else if (c == '(') 351 cmntcnt++; 352 else if (c == ')') 353 { 354 if (cmntcnt <= 0) 355 { 356 usrerr("Unbalanced ')'"); 357 return (NULL); 358 } 359 else 360 { 361 cmntcnt--; 362 continue; 363 } 364 } 365 if (cmntcnt > 0) 366 continue; 367 else if (c == '<') 368 { 369 brccnt++; 370 if (brccnt == 1) 371 { 372 /* we prefer using machine readable name */ 373 q = buf; 374 *q = '\0'; 375 continue; 376 } 377 } 378 else if (c == '>') 379 { 380 if (brccnt <= 0) 381 { 382 usrerr("Unbalanced `>'"); 383 return (NULL); 384 } 385 else 386 brccnt--; 387 if (brccnt <= 0) 388 continue; 389 } 390 391 /* 392 ** Turn "at" into "@", 393 ** but only if "at" is a word in and to itself. 394 ** By the way, I violate the ARPANET RFC-733 395 ** standard here, by assuming that 'space' delimits 396 ** atoms. I assume that is just a mistake, since 397 ** it violates the spirit of the semantics 398 ** of the document..... 399 */ 400 401 if (space && (c == 'a' || c == 'A') && 402 (p[0] == 't' || p[0] == 'T') && 403 (any(p[1], "()<>@,;:\\\"") || p[1] <= 040)) 404 { 405 c = '@'; 406 p++; 407 } 408 409 /* skip blanks */ 410 if (((c & 0200) != 0 || !isspace(c)) && cmntcnt <= 0) 411 { 412 if (q >= buflim) 413 { 414 usrerr("Address too long"); 415 return (NULL); 416 } 417 *q++ = c; 418 } 419 space = isspace(c); 420 } 421 *q = '\0'; 422 if (c == '\0') 423 p--; 424 if (cmntcnt > 0) 425 usrerr("Unbalanced '('"); 426 else if (quotemode) 427 usrerr("Unbalanced '\"'"); 428 else if (brccnt > 0) 429 usrerr("Unbalanced '<'"); 430 else if (buf[0] != '\0') 431 return (p); 432 return (NULL); 433 } 434