xmpp/layer2.go
changeset 143 62166e57800e
child 145 21a390dd3506
equal deleted inserted replaced
142:0ff033eed887 143:62166e57800e
       
     1 // This layer of the XMPP protocol translates between bytes and XMLish
       
     2 // structures.
       
     3 
       
     4 package xmpp
       
     5 
       
     6 import (
       
     7 	"io"
       
     8 	"reflect"
       
     9 	"encoding/xml"
       
    10 	"fmt"
       
    11 	"strings"
       
    12 )
       
    13 
       
    14 func readXml(r io.Reader, ch chan<- interface{},
       
    15 	extStanza map[xml.Name]reflect.Type) {
       
    16 	if _, ok := Debug.(*noLog); !ok {
       
    17 		pr, pw := io.Pipe()
       
    18 		go tee(r, pw, "S: ")
       
    19 		r = pr
       
    20 	}
       
    21 	defer close(ch)
       
    22 
       
    23 	// This trick loads our namespaces into the parser.
       
    24 	nsstr := fmt.Sprintf(`<a xmlns="%s" xmlns:stream="%s">`,
       
    25 		NsClient, NsStream)
       
    26 	nsrdr := strings.NewReader(nsstr)
       
    27 	p := xml.NewDecoder(io.MultiReader(nsrdr, r))
       
    28 	p.Token()
       
    29 
       
    30 Loop:
       
    31 	for {
       
    32 		// Sniff the next token on the stream.
       
    33 		t, err := p.Token()
       
    34 		if t == nil {
       
    35 			if err != io.EOF {
       
    36 				Warn.Logf("read: %s", err)
       
    37 			}
       
    38 			break
       
    39 		}
       
    40 		var se xml.StartElement
       
    41 		var ok bool
       
    42 		if se, ok = t.(xml.StartElement); !ok {
       
    43 			continue
       
    44 		}
       
    45 
       
    46 		// Allocate the appropriate structure for this token.
       
    47 		var obj interface{}
       
    48 		switch se.Name.Space + " " + se.Name.Local {
       
    49 		case NsStream + " stream":
       
    50 			st, err := parseStream(se)
       
    51 			if err != nil {
       
    52 				Warn.Logf("unmarshal stream: %s", err)
       
    53 				break Loop
       
    54 			}
       
    55 			ch <- st
       
    56 			continue
       
    57 		case "stream error", NsStream + " error":
       
    58 			obj = &streamError{}
       
    59 		case NsStream + " features":
       
    60 			obj = &Features{}
       
    61 		case NsTLS + " proceed", NsTLS + " failure":
       
    62 			obj = &starttls{}
       
    63 		case NsSASL + " challenge", NsSASL + " failure",
       
    64 			NsSASL + " success":
       
    65 			obj = &auth{}
       
    66 		case NsClient + " iq":
       
    67 			obj = &Iq{}
       
    68 		case NsClient + " message":
       
    69 			obj = &Message{}
       
    70 		case NsClient + " presence":
       
    71 			obj = &Presence{}
       
    72 		default:
       
    73 			obj = &Generic{}
       
    74 			Info.Logf("Ignoring unrecognized: %s %s", se.Name.Space,
       
    75 				se.Name.Local)
       
    76 		}
       
    77 
       
    78 		// Read the complete XML stanza.
       
    79 		err = p.DecodeElement(obj, &se)
       
    80 		if err != nil {
       
    81 			Warn.Logf("unmarshal: %s", err)
       
    82 			break Loop
       
    83 		}
       
    84 
       
    85 		// If it's a Stanza, we try to unmarshal its innerxml
       
    86 		// into objects of the appropriate respective
       
    87 		// types. This is specified by our extensions.
       
    88 		if st, ok := obj.(Stanza); ok {
       
    89 			err = parseExtended(st.GetHeader(), extStanza)
       
    90 			if err != nil {
       
    91 				Warn.Logf("ext unmarshal: %s", err)
       
    92 				break Loop
       
    93 			}
       
    94 		}
       
    95 
       
    96 		// Put it on the channel.
       
    97 		ch <- obj
       
    98 	}
       
    99 }
       
   100 
       
   101 func parseExtended(st *Header, extStanza map[xml.Name]reflect.Type) error {
       
   102 	// Now parse the stanza's innerxml to find the string that we
       
   103 	// can unmarshal this nested element from.
       
   104 	reader := strings.NewReader(st.Innerxml)
       
   105 	p := xml.NewDecoder(reader)
       
   106 	for {
       
   107 		t, err := p.Token()
       
   108 		if err == io.EOF {
       
   109 			break
       
   110 		}
       
   111 		if err != nil {
       
   112 			return err
       
   113 		}
       
   114 		if se, ok := t.(xml.StartElement); ok {
       
   115 			if typ, ok := extStanza[se.Name]; ok {
       
   116 				nested := reflect.New(typ).Interface()
       
   117 
       
   118 				// Unmarshal the nested element and
       
   119 				// stuff it back into the stanza.
       
   120 				err := p.DecodeElement(nested, &se)
       
   121 				if err != nil {
       
   122 					return err
       
   123 				}
       
   124 				st.Nested = append(st.Nested, nested)
       
   125 			}
       
   126 		}
       
   127 	}
       
   128 
       
   129 	return nil
       
   130 }
       
   131 
       
   132 func writeXml(w io.Writer, ch <-chan interface{}) {
       
   133 	if _, ok := Debug.(*noLog); !ok {
       
   134 		pr, pw := io.Pipe()
       
   135 		go tee(pr, w, "C: ")
       
   136 		w = pw
       
   137 	}
       
   138 	defer func(w io.Writer) {
       
   139 		if c, ok := w.(io.Closer); ok {
       
   140 			c.Close()
       
   141 		}
       
   142 	}(w)
       
   143 
       
   144 	enc := xml.NewEncoder(w)
       
   145 
       
   146 	for obj := range ch {
       
   147 		if st, ok := obj.(*stream); ok {
       
   148 			_, err := w.Write([]byte(st.String()))
       
   149 			if err != nil {
       
   150 				Warn.Logf("write: %s", err)
       
   151 			}
       
   152 		} else {
       
   153 			err := enc.Encode(obj)
       
   154 			if err != nil {
       
   155 				Warn.Logf("marshal: %s", err)
       
   156 				break
       
   157 			}
       
   158 		}
       
   159 	}
       
   160 }