1 # include <stdio.h> 2 # include <ctype.h> 3 # include "dlvrmail.h" 4 5 static char SccsId[] = "@(#)parseaddr.c 1.4 10/11/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 { 352 cmntcnt++; 353 continue; 354 } 355 else if (c == ')') 356 { 357 if (cmntcnt <= 0) 358 { 359 usrerr("Unbalanced ')'"); 360 return (NULL); 361 } 362 else 363 { 364 cmntcnt--; 365 continue; 366 } 367 } 368 else if (c == '<') 369 { 370 brccnt++; 371 if (brccnt == 1) 372 { 373 /* we prefer using machine readable name */ 374 q = buf; 375 *q = '\0'; 376 continue; 377 } 378 } 379 else if (c == '>') 380 { 381 if (brccnt <= 0) 382 { 383 usrerr("Unbalanced `>'"); 384 return (NULL); 385 } 386 else 387 brccnt--; 388 if (brccnt <= 0) 389 continue; 390 } 391 392 /* 393 ** Turn "at" into "@", 394 ** but only if "at" is a word. 395 ** By the way, I violate the ARPANET RFC-733 396 ** standard here, by assuming that 'space' delimits 397 ** atoms. I assume that is just a mistake, since 398 ** it violates the spirit of the semantics 399 ** of the document..... 400 */ 401 402 if (space && (c == 'a' || c == 'A') && 403 (p[0] == 't' || p[0] == 'T') && 404 (any(p[1], "()<>@,;:\\\"") || p[1] <= 040)) 405 { 406 c = '@'; 407 p++; 408 } 409 410 /* skip blanks */ 411 if (((c & 0200) != 0 || !isspace(c)) && cmntcnt <= 0) 412 { 413 if (q >= buflim) 414 { 415 usrerr("Address too long"); 416 return (NULL); 417 } 418 *q++ = c; 419 } 420 space = isspace(c); 421 } 422 *q = '\0'; 423 if (c == '\0') 424 p--; 425 if (cmntcnt > 0) 426 usrerr("Unbalanced '('"); 427 else if (quotemode) 428 usrerr("Unbalanced '\"'"); 429 else if (brccnt > 0) 430 usrerr("Unbalanced '<'"); 431 else if (buf[0] != '\0') 432 return (p); 433 return (NULL); 434 } 435