1implement Message; 2 3include "sys.m"; 4 sys: Sys; 5 6include "string.m"; 7 S : String; 8 9include "bufio.m"; 10 B : Bufio; 11 Iobuf: import B; 12 13include "message.m"; 14 msg: Message; 15 16msglog: ref Sys->FD; 17 18init(bufio: Bufio, smod: String) 19{ 20 sys = load Sys Sys->PATH; 21 S = smod; 22 B = bufio; 23} 24 25sptab : con " \t"; 26crlf : con "\r\n"; 27 28Msg.newmsg() : ref Msg 29{ 30 return ref Msg("", nil, nil, nil, 0); 31} 32 33# Read a message header from fd and return a Msg 34# the header fields. 35# If withprefix is true, read one line first and put it 36# in the prefixline field of the Msg (without terminating \r\n) 37# Return nil if there is a read error or eof before the 38# header is completely read. 39Msg.readhdr(io: ref Iobuf, withprefix: int) : (ref Msg, string) 40{ 41 m := Msg.newmsg(); 42 l : list of Nameval = nil; 43 needprefix := withprefix; 44 for(;;) { 45 line := getline(io); 46 n := len line; 47 if(n == 0) { 48 if(withprefix && m.prefixline != "") 49 break; 50 return(nil, "msg read hdr error: no header"); 51 } 52 if(line[n-1] != '\n') 53 return (m, "msg read hdr error: incomplete header"); 54 if(n >= 2 && line[n-2] == '\r') 55 line = line[0:n-2]; 56 else 57 line = line[0:n-1]; 58 if(needprefix) { 59 m.prefixline = line; 60 needprefix = 0; 61 } 62 else { 63 if(line == "") 64 break; 65 if(S->in(line[0], sptab)) { 66 if(l == nil) 67 continue; 68 nv := hd l; 69 l = Nameval(nv.name, nv.value + " " + S->drop(line, sptab)) :: tl l; 70 } 71 else { 72 (nam, val) := S->splitl(line, ":"); 73 if(val == nil) 74 continue; # no colon 75 l = Nameval(S->tolower(nam), S->drop(val[1:], sptab)) :: l; 76 } 77 } 78 } 79 nh := len l; 80 if(nh > 0) { 81 m.fields = array[nh] of Nameval; 82 for(i := nh-1; i >= 0; i--) { 83 m.fields[i] = hd l; 84 l = tl l; 85 } 86 } 87 return (m, ""); 88} 89 90glbuf := array[300] of byte; 91 92# Like io.gets('\n'), but assume Latin-1 instead of UTF encoding 93getline(io: ref Iobuf): string 94{ 95 imax := len glbuf - 1; 96 for(i := 0; i < imax; ) { 97 c := io.getb(); 98 if(c < 0) 99 break; 100 if(c < 128) 101 glbuf[i++] = byte c; 102 else 103 i += sys->char2byte(c, glbuf, i); 104 if(c == '\n') 105 break; 106 if(i == imax) { 107 imax += 100; 108 if(imax > 1000) 109 break; # Header lines aren't supposed to be > 1000 110 newglbuf := array[imax] of byte; 111 newglbuf[0:] = glbuf[0:i]; 112 glbuf = newglbuf; 113 } 114 } 115 ans := string glbuf[0:i]; 116 return ans; 117} 118 119Bbufsize: con 8000; 120 121# Read the body of the message, assuming the header has been processed. 122# If content-length has been specified, read exactly that many bytes 123# or until eof; else read until done. 124# Return "" if all is OK, else return an error string. 125Msg.readbody(m: self ref Msg, io: ref Iobuf) : string 126{ 127 (clfnd, cl) := m.fieldval("content-length"); 128 if(clfnd) { 129 clen := int cl; 130 if(clen > 0) { 131 m.body = array[clen] of byte; 132 n := B->io.read(m.body, clen); 133 m.bodylen = n; 134 if(n != clen) 135 return "short read"; 136 } 137 } 138 else { 139 m.body = array[Bbufsize] of byte; 140 curlen := 0; 141 for(;;) { 142 avail := len m.body - curlen; 143 if(avail <= 0) { 144 newa := array[len m.body + Bbufsize] of byte; 145 if(curlen > 0) 146 newa[0:] = m.body[0:curlen]; 147 m.body = newa; 148 avail = Bbufsize; 149 } 150 n := B->io.read(m.body[curlen:], avail); 151 if(n < 0) 152 return sys->sprint("readbody error %r"); 153 if(n == 0) 154 break; 155 else 156 curlen += n; 157 } 158 m.bodylen = curlen; 159 } 160 return ""; 161} 162 163# Look for name (lowercase) in the fields of m 164# and (1, field value) if found, or (0,"") if not. 165# If multiple fields with the same name exist, 166# the value is defined as the comma separated list 167# of all such values. 168Msg.fieldval(m: self ref Msg, name: string) : (int, string) 169{ 170 n := len m.fields; 171 ans := ""; 172 found := 0; 173 for(i := 0; i < n; i++) { 174 if(m.fields[i].name == name) { 175 v := m.fields[i].value; 176 if(found) 177 ans = ans + ", " + v; 178 else 179 ans = v; 180 found = 1; 181 } 182 } 183 return (found, ans); 184} 185 186Msg.addhdrs(m: self ref Msg, hdrs: list of Nameval) 187{ 188 nh := len hdrs; 189 if(nh == 0) 190 return; 191 onh := len m.fields; 192 newa := array[nh + onh] of Nameval; 193 newa[0:] = m.fields; 194 i := onh; 195 while(hdrs != nil) { 196 newa[i++] = hd hdrs; 197 hdrs = tl hdrs; 198 } 199 m.fields = newa; 200} 201 202Msg.update(m: self ref Msg, name, value: string) 203{ 204 for(i := 0; i < len m.fields; i++) 205 if(m.fields[i].name == name) { 206 m.fields[i] = Nameval(name, value); 207 return; 208 } 209 m.addhdrs(Nameval(name, value) :: nil); 210} 211 212Msg.header(m: self ref Msg) : string 213{ 214 s := ""; 215 for(i := 0; i < len m.fields; i++) { 216 nv := m.fields[i]; 217 s += nv.name + ": " + nv.value + "\n"; 218 } 219 return s; 220} 221 222Msg.writemsg(m: self ref Msg, io: ref Iobuf) : string 223{ 224 n := 0; 225 if(m.prefixline != nil) { 226 n = B->io.puts(m.prefixline); 227 if(n >= 0) 228 n = B->io.puts(crlf); 229 } 230 for(i := 0; i < len m.fields; i++) { 231 nv := m.fields[i]; 232 if(n >= 0) 233 n = B->io.puts(nv.name); 234 if(n >= 0) 235 n = B->io.puts(": "); 236 if(n >= 0) 237 n = B->io.puts(nv.value); 238 if(n >= 0) 239 n = B->io.puts(crlf); 240 } 241 if(n >= 0) 242 n = B->io.puts(crlf); 243 if(n >= 0 && m.bodylen > 0) 244 n = B->io.write(m.body, m.bodylen); 245 if(n < 0) 246 return sys->sprint("msg write error: %r"); 247 B->io.flush(); 248 return ""; 249} 250