github.com/slspeek/camlistore_namedsearch@v0.0.0-20140519202248-ed6f70f7721a/pkg/server/root.go (about) 1 /* 2 Copyright 2011 Google Inc. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package server 18 19 import ( 20 "encoding/json" 21 "fmt" 22 "log" 23 "net/http" 24 "os" 25 "sort" 26 "sync" 27 "time" 28 29 "camlistore.org/pkg/auth" 30 "camlistore.org/pkg/blobserver" 31 "camlistore.org/pkg/buildinfo" 32 "camlistore.org/pkg/images" 33 "camlistore.org/pkg/jsonconfig" 34 "camlistore.org/pkg/osutil" 35 "camlistore.org/pkg/search" 36 ) 37 38 // RootHandler handles serving the about/splash page. 39 type RootHandler struct { 40 // Stealth determines whether we hide from non-authenticated 41 // clients. 42 Stealth bool 43 44 OwnerName string // for display purposes only. 45 Username string // default user for mobile setup. 46 47 // URL prefixes (path or full URL) to the primary blob and 48 // search root. 49 BlobRoot string 50 SearchRoot string 51 statusRoot string 52 Prefix string // root handler's prefix 53 54 Storage blobserver.Storage // of BlobRoot, or nil 55 56 searchInitOnce sync.Once // runs searchInit, which populates searchHandler 57 searchInit func() 58 searchHandler *search.Handler // of SearchRoot, or nil 59 60 ui *UIHandler // or nil, if none configured 61 sync []*SyncHandler // list of configured sync handlers, for discovery. 62 } 63 64 func (rh *RootHandler) SearchHandler() (h *search.Handler, ok bool) { 65 rh.searchInitOnce.Do(rh.searchInit) 66 return rh.searchHandler, rh.searchHandler != nil 67 } 68 69 func init() { 70 blobserver.RegisterHandlerConstructor("root", newRootFromConfig) 71 } 72 73 func newRootFromConfig(ld blobserver.Loader, conf jsonconfig.Obj) (h http.Handler, err error) { 74 username, err := getUserName() 75 if err != nil { 76 return 77 } 78 root := &RootHandler{ 79 BlobRoot: conf.OptionalString("blobRoot", ""), 80 SearchRoot: conf.OptionalString("searchRoot", ""), 81 OwnerName: conf.OptionalString("ownerName", username), 82 Username: osutil.Username(), 83 Prefix: ld.MyPrefix(), 84 } 85 root.Stealth = conf.OptionalBool("stealth", false) 86 root.statusRoot = conf.OptionalString("statusRoot", "") 87 if err = conf.Validate(); err != nil { 88 return 89 } 90 91 if root.BlobRoot != "" { 92 bs, err := ld.GetStorage(root.BlobRoot) 93 if err != nil { 94 return nil, fmt.Errorf("Root handler's blobRoot of %q error: %v", root.BlobRoot, err) 95 } 96 root.Storage = bs 97 } 98 99 root.searchInit = func() {} 100 if root.SearchRoot != "" { 101 prefix := root.SearchRoot 102 if t := ld.GetHandlerType(prefix); t != "search" { 103 if t == "" { 104 return nil, fmt.Errorf("root handler's searchRoot of %q is invalid and doesn't refer to a declared handler", prefix) 105 } 106 return nil, fmt.Errorf("root handler's searchRoot of %q is of type %q, not %q", prefix, t, "search") 107 } 108 root.searchInit = func() { 109 h, err := ld.GetHandler(prefix) 110 if err != nil { 111 log.Fatalf("Error fetching SearchRoot at %q: %v", prefix, err) 112 } 113 root.searchHandler = h.(*search.Handler) 114 root.searchInit = nil 115 } 116 } 117 118 return root, nil 119 } 120 121 func (rh *RootHandler) registerUIHandler(h *UIHandler) { 122 rh.ui = h 123 } 124 125 func (rh *RootHandler) registerSyncHandler(h *SyncHandler) { 126 rh.sync = append(rh.sync, h) 127 sort.Sort(byFromTo(rh.sync)) 128 } 129 130 func (rh *RootHandler) ServeHTTP(rw http.ResponseWriter, req *http.Request) { 131 if wantsDiscovery(req) { 132 if auth.Allowed(req, auth.OpDiscovery) { 133 rh.serveDiscovery(rw, req) 134 return 135 } 136 if !rh.Stealth { 137 http.Error(rw, "Unauthorized", http.StatusUnauthorized) 138 } 139 return 140 } 141 142 if rh.Stealth { 143 return 144 } 145 if req.URL.Path == "/favicon.ico" { 146 serveStaticFile(rw, req, Files, "favicon.ico") 147 return 148 } 149 f := func(p string, a ...interface{}) { 150 fmt.Fprintf(rw, p, a...) 151 } 152 f("<html><body><p>This is camlistored (%s), a "+ 153 "<a href='http://camlistore.org'>Camlistore</a> server.</p>", buildinfo.Version()) 154 if auth.IsLocalhost(req) && !isDevServer() { 155 f("<p>If you're coming from localhost, configure your Camlistore server at <a href='/setup'>/setup</a>.</p>") 156 } 157 if rh.ui != nil { 158 f("<p>To manage your content, access the <a href='%s'>%s</a>.</p>", rh.ui.prefix, rh.ui.prefix) 159 } 160 if rh.statusRoot != "" { 161 f("<p>To view status, see <a href='%s'>%s</a>", rh.statusRoot, rh.statusRoot) 162 } 163 fmt.Fprintf(rw, "</body></html>") 164 } 165 166 func isDevServer() bool { 167 return os.Getenv("CAMLI_DEV_CAMLI_ROOT") != "" 168 } 169 170 type byFromTo []*SyncHandler 171 172 func (b byFromTo) Len() int { return len(b) } 173 func (b byFromTo) Swap(i, j int) { b[i], b[j] = b[j], b[i] } 174 func (b byFromTo) Less(i, j int) bool { 175 if b[i].fromName < b[j].fromName { 176 return true 177 } 178 return b[i].fromName == b[j].fromName && b[i].toName < b[j].toName 179 } 180 181 func (rh *RootHandler) serveDiscovery(rw http.ResponseWriter, req *http.Request) { 182 m := map[string]interface{}{ 183 "blobRoot": rh.BlobRoot, 184 "searchRoot": rh.SearchRoot, 185 "ownerName": rh.OwnerName, 186 "username": rh.Username, 187 "statusRoot": rh.statusRoot, 188 "wsAuthToken": auth.ProcessRandom(), 189 "thumbVersion": images.ThumbnailVersion(), 190 } 191 if gener, ok := rh.Storage.(blobserver.Generationer); ok { 192 initTime, gen, err := gener.StorageGeneration() 193 if err != nil { 194 m["storageGenerationError"] = err.Error() 195 } else { 196 m["storageInitTime"] = initTime.UTC().Format(time.RFC3339) 197 m["storageGeneration"] = gen 198 } 199 } else { 200 log.Printf("Storage type %T is not a blobserver.Generationer; not sending storageGeneration", rh.Storage) 201 } 202 if rh.ui != nil { 203 rh.ui.populateDiscoveryMap(m) 204 } 205 if len(rh.sync) > 0 { 206 var syncHandlers []map[string]interface{} 207 for _, sh := range rh.sync { 208 syncHandlers = append(syncHandlers, sh.discoveryMap()) 209 } 210 m["syncHandlers"] = syncHandlers 211 } 212 discoveryHelper(rw, req, m) 213 } 214 215 func discoveryHelper(rw http.ResponseWriter, req *http.Request, m map[string]interface{}) { 216 rw.Header().Set("Content-Type", "text/javascript") 217 if cb := req.FormValue("cb"); identOrDotPattern.MatchString(cb) { 218 fmt.Fprintf(rw, "%s(", cb) 219 defer rw.Write([]byte(");\n")) 220 } else if v := req.FormValue("var"); identOrDotPattern.MatchString(v) { 221 fmt.Fprintf(rw, "%s = ", v) 222 defer rw.Write([]byte(";\n")) 223 } 224 bytes, _ := json.MarshalIndent(m, "", " ") 225 rw.Write(bytes) 226 }