| OLD | NEW |
| 1 package main | 1 package main |
| 2 | 2 |
| 3 import ( | 3 import ( |
| 4 "bytes" | 4 "bytes" |
| 5 "crypto/md5" | 5 "crypto/md5" |
| 6 "database/sql" | 6 "database/sql" |
| 7 "encoding/base64" | 7 "encoding/base64" |
| 8 "encoding/binary" | 8 "encoding/binary" |
| 9 "encoding/json" | 9 "encoding/json" |
| 10 "flag" | 10 "flag" |
| (...skipping 139 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 150 if err != nil { | 150 if err != nil { |
| 151 panic(err) | 151 panic(err) |
| 152 } | 152 } |
| 153 gypTemplate, err = template.ParseFiles(filepath.Join(cwd, "templates/tem
plate.gyp")) | 153 gypTemplate, err = template.ParseFiles(filepath.Join(cwd, "templates/tem
plate.gyp")) |
| 154 if err != nil { | 154 if err != nil { |
| 155 panic(err) | 155 panic(err) |
| 156 } | 156 } |
| 157 indexTemplate, err = htemplate.ParseFiles( | 157 indexTemplate, err = htemplate.ParseFiles( |
| 158 filepath.Join(cwd, "templates/index.html"), | 158 filepath.Join(cwd, "templates/index.html"), |
| 159 filepath.Join(cwd, "templates/titlebar.html"), | 159 filepath.Join(cwd, "templates/titlebar.html"), |
| 160 filepath.Join(cwd, "templates/sidebar.html"), |
| 160 filepath.Join(cwd, "templates/content.html"), | 161 filepath.Join(cwd, "templates/content.html"), |
| 161 filepath.Join(cwd, "templates/headercommon.html"), | 162 filepath.Join(cwd, "templates/headercommon.html"), |
| 162 filepath.Join(cwd, "templates/footercommon.html"), | 163 filepath.Join(cwd, "templates/footercommon.html"), |
| 163 ) | 164 ) |
| 164 if err != nil { | 165 if err != nil { |
| 165 panic(err) | 166 panic(err) |
| 166 } | 167 } |
| 167 iframeTemplate, err = htemplate.ParseFiles( | 168 iframeTemplate, err = htemplate.ParseFiles( |
| 168 filepath.Join(cwd, "templates/iframe.html"), | 169 filepath.Join(cwd, "templates/iframe.html"), |
| 169 filepath.Join(cwd, "templates/content.html"), | 170 filepath.Join(cwd, "templates/content.html"), |
| 170 filepath.Join(cwd, "templates/headercommon.html"), | 171 filepath.Join(cwd, "templates/headercommon.html"), |
| 171 filepath.Join(cwd, "templates/footercommon.html"), | 172 filepath.Join(cwd, "templates/footercommon.html"), |
| 172 ) | 173 ) |
| 173 if err != nil { | 174 if err != nil { |
| 174 panic(err) | 175 panic(err) |
| 175 } | 176 } |
| 176 recentTemplate, err = htemplate.ParseFiles( | 177 recentTemplate, err = htemplate.ParseFiles( |
| 177 filepath.Join(cwd, "templates/recent.html"), | 178 filepath.Join(cwd, "templates/recent.html"), |
| 178 filepath.Join(cwd, "templates/titlebar.html"), | 179 filepath.Join(cwd, "templates/titlebar.html"), |
| 180 filepath.Join(cwd, "templates/sidebar.html"), |
| 179 filepath.Join(cwd, "templates/headercommon.html"), | 181 filepath.Join(cwd, "templates/headercommon.html"), |
| 180 filepath.Join(cwd, "templates/footercommon.html"), | 182 filepath.Join(cwd, "templates/footercommon.html"), |
| 181 ) | 183 ) |
| 182 if err != nil { | 184 if err != nil { |
| 183 panic(err) | 185 panic(err) |
| 184 } | 186 } |
| 185 workspaceTemplate, err = htemplate.ParseFiles( | 187 workspaceTemplate, err = htemplate.ParseFiles( |
| 186 filepath.Join(cwd, "templates/workspace.html"), | 188 filepath.Join(cwd, "templates/workspace.html"), |
| 187 filepath.Join(cwd, "templates/titlebar.html"), | 189 filepath.Join(cwd, "templates/titlebar.html"), |
| 190 filepath.Join(cwd, "templates/sidebar.html"), |
| 188 filepath.Join(cwd, "templates/content.html"), | 191 filepath.Join(cwd, "templates/content.html"), |
| 189 filepath.Join(cwd, "templates/headercommon.html"), | 192 filepath.Join(cwd, "templates/headercommon.html"), |
| 190 filepath.Join(cwd, "templates/footercommon.html"), | 193 filepath.Join(cwd, "templates/footercommon.html"), |
| 191 ) | 194 ) |
| 192 if err != nil { | 195 if err != nil { |
| 193 panic(err) | 196 panic(err) |
| 194 } | 197 } |
| 195 | 198 |
| 196 // The git command returns output of the format: | 199 // The git command returns output of the format: |
| 197 // | 200 // |
| (...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 245 )` | 248 )` |
| 246 _, err = db.Exec(sql) | 249 _, err = db.Exec(sql) |
| 247 if err != nil { | 250 if err != nil { |
| 248 log.Printf("Info: status creating sqlite table for sourc
es: %q\n", err) | 251 log.Printf("Info: status creating sqlite table for sourc
es: %q\n", err) |
| 249 } | 252 } |
| 250 | 253 |
| 251 sql = `CREATE TABLE IF NOT EXISTS webtry ( | 254 sql = `CREATE TABLE IF NOT EXISTS webtry ( |
| 252 code TEXT DEFAULT '' NOT NULL, | 255 code TEXT DEFAULT '' NOT NULL, |
| 253 create_ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL, | 256 create_ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL, |
| 254 hash CHAR(64) DEFAULT '' NOT NULL, | 257 hash CHAR(64) DEFAULT '' NOT NULL, |
| 258 width INTEGER DEFAULT 256 NOT
NULL, |
| 259 height INTEGER DEFAULT 256 NOT
NULL, |
| 255 source_image_id INTEGER DEFAULT 0 NOT NULL, | 260 source_image_id INTEGER DEFAULT 0 NOT NULL, |
| 256 | 261 |
| 257 PRIMARY KEY(hash) | 262 PRIMARY KEY(hash) |
| 258 )` | 263 )` |
| 259 _, err = db.Exec(sql) | 264 _, err = db.Exec(sql) |
| 260 if err != nil { | 265 if err != nil { |
| 261 log.Printf("Info: status creating sqlite table for webtr
y: %q\n", err) | 266 log.Printf("Info: status creating sqlite table for webtr
y: %q\n", err) |
| 262 } | 267 } |
| 263 | 268 |
| 264 sql = `CREATE TABLE IF NOT EXISTS workspace ( | 269 sql = `CREATE TABLE IF NOT EXISTS workspace ( |
| 265 name CHAR(64) DEFAULT '' NOT NULL, | 270 name CHAR(64) DEFAULT '' NOT NULL, |
| 266 create_ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL, | 271 create_ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL, |
| 267 PRIMARY KEY(name) | 272 PRIMARY KEY(name) |
| 268 )` | 273 )` |
| 269 _, err = db.Exec(sql) | 274 _, err = db.Exec(sql) |
| 270 if err != nil { | 275 if err != nil { |
| 271 log.Printf("Info: status creating sqlite table for works
pace: %q\n", err) | 276 log.Printf("Info: status creating sqlite table for works
pace: %q\n", err) |
| 272 } | 277 } |
| 273 | 278 |
| 274 sql = `CREATE TABLE IF NOT EXISTS workspacetry ( | 279 sql = `CREATE TABLE IF NOT EXISTS workspacetry ( |
| 275 name CHAR(64) DEFAULT '' NOT NULL, | 280 name CHAR(64) DEFAULT '' NOT NULL, |
| 276 create_ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL, | 281 create_ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL, |
| 277 hash CHAR(64) DEFAULT '' NOT NULL, | 282 hash CHAR(64) DEFAULT '' NOT NULL, |
| 283 width INTEGER DEFAULT 256 NOT NULL, |
| 284 height INTEGER DEFAULT 256 NOT NULL, |
| 278 hidden INTEGER DEFAULT 0 NOT NULL, | 285 hidden INTEGER DEFAULT 0 NOT NULL, |
| 279 source_image_id INTEGER DEFAULT 0 NOT NULL, | 286 source_image_id INTEGER DEFAULT 0 NOT NULL, |
| 280 | 287 |
| 281 FOREIGN KEY (name) REFERENCES workspace(name) | 288 FOREIGN KEY (name) REFERENCES workspace(name) |
| 282 )` | 289 )` |
| 283 _, err = db.Exec(sql) | 290 _, err = db.Exec(sql) |
| 284 if err != nil { | 291 if err != nil { |
| 285 log.Printf("Info: status creating sqlite table for works
pace try: %q\n", err) | 292 log.Printf("Info: status creating sqlite table for works
pace try: %q\n", err) |
| 286 } | 293 } |
| 287 } | 294 } |
| (...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 338 // Titlebar is used in titlebar template expansion. | 345 // Titlebar is used in titlebar template expansion. |
| 339 type Titlebar struct { | 346 type Titlebar struct { |
| 340 GitHash string | 347 GitHash string |
| 341 GitInfo string | 348 GitInfo string |
| 342 } | 349 } |
| 343 | 350 |
| 344 // userCode is used in template expansion. | 351 // userCode is used in template expansion. |
| 345 type userCode struct { | 352 type userCode struct { |
| 346 Code string | 353 Code string |
| 347 Hash string | 354 Hash string |
| 355 Width int |
| 356 Height int |
| 348 Source int | 357 Source int |
| 349 Titlebar Titlebar | 358 Titlebar Titlebar |
| 350 } | 359 } |
| 351 | 360 |
| 352 // writeTemplate creates a given output file and writes the template | 361 // writeTemplate creates a given output file and writes the template |
| 353 // result there. | 362 // result there. |
| 354 func writeTemplate(filename string, t *template.Template, context interface{}) e
rror { | 363 func writeTemplate(filename string, t *template.Template, context interface{}) e
rror { |
| 355 f, err := os.Create(filename) | 364 f, err := os.Create(filename) |
| 356 if err != nil { | 365 if err != nil { |
| 357 return err | 366 return err |
| 358 } | 367 } |
| 359 defer f.Close() | 368 defer f.Close() |
| 360 return t.Execute(f, context) | 369 return t.Execute(f, context) |
| 361 } | 370 } |
| 362 | 371 |
| 363 // expandToFile expands the template and writes the result to the file. | 372 // expandToFile expands the template and writes the result to the file. |
| 364 func expandToFile(filename string, code string, t *template.Template) error { | 373 func expandToFile(filename string, code string, t *template.Template) error { |
| 365 » return writeTemplate(filename, t, userCode{Code: code, Titlebar: Titleba
r{GitHash: gitHash, GitInfo: gitInfo}}) | 374 » return writeTemplate(filename, t, userCode{ |
| 375 » » Code: code, |
| 376 » » Titlebar: Titlebar{GitHash: gitHash, GitInfo: gitInfo}, |
| 377 » }) |
| 366 } | 378 } |
| 367 | 379 |
| 368 // expandCode expands the template into a file and calculates the MD5 hash. | 380 // expandCode expands the template into a file and calculates the MD5 hash. |
| 369 func expandCode(code string, source int) (string, error) { | 381 // We include the width and height here so that a single hash can capture |
| 382 // both the code and the supplied width/height parameters. |
| 383 func expandCode(code string, source int, width, height int) (string, error) { |
| 370 // in order to support fonts in the chroot jail, we need to make sure | 384 // in order to support fonts in the chroot jail, we need to make sure |
| 371 // we're using portable typefaces. | 385 // we're using portable typefaces. |
| 372 // TODO(humper): Make this more robust, supporting things like setTypef
ace | 386 // TODO(humper): Make this more robust, supporting things like setTypef
ace |
| 373 | 387 |
| 374 inputCodeLines := strings.Split(code, "\n") | 388 inputCodeLines := strings.Split(code, "\n") |
| 375 » outputCodeLines := []string{"DECLARE_bool(portableFonts);"} | 389 » outputCodeLines := []string{"DECLARE_bool(portableFonts);", fmt.Sprintf(
"// WxH: %d, %d", width, height)} |
| 376 for _, line := range inputCodeLines { | 390 for _, line := range inputCodeLines { |
| 377 outputCodeLines = append(outputCodeLines, line) | 391 outputCodeLines = append(outputCodeLines, line) |
| 378 if strings.HasPrefix(strings.TrimSpace(line), "SkPaint p") { | 392 if strings.HasPrefix(strings.TrimSpace(line), "SkPaint p") { |
| 379 outputCodeLines = append(outputCodeLines, "FLAGS_portabl
eFonts = true;") | 393 outputCodeLines = append(outputCodeLines, "FLAGS_portabl
eFonts = true;") |
| 380 outputCodeLines = append(outputCodeLines, "sk_tool_utils
::set_portable_typeface(&p, \"Helvetica\", SkTypeface::kNormal);") | 394 outputCodeLines = append(outputCodeLines, "sk_tool_utils
::set_portable_typeface(&p, \"Helvetica\", SkTypeface::kNormal);") |
| 381 } | 395 } |
| 382 } | 396 } |
| 383 | 397 |
| 384 fontFriendlyCode := strings.Join(outputCodeLines, "\n") | 398 fontFriendlyCode := strings.Join(outputCodeLines, "\n") |
| 385 | 399 |
| (...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 444 log.Printf("Error: %s\n%s", message, err.Error()) | 458 log.Printf("Error: %s\n%s", message, err.Error()) |
| 445 resp, err := json.Marshal(m) | 459 resp, err := json.Marshal(m) |
| 446 if err != nil { | 460 if err != nil { |
| 447 http.Error(w, "Failed to serialize a response", 500) | 461 http.Error(w, "Failed to serialize a response", 500) |
| 448 return | 462 return |
| 449 } | 463 } |
| 450 w.Header().Set("Content-Type", "text/plain") | 464 w.Header().Set("Content-Type", "text/plain") |
| 451 w.Write(resp) | 465 w.Write(resp) |
| 452 } | 466 } |
| 453 | 467 |
| 454 func writeToDatabase(hash string, code string, workspaceName string, source int)
{ | 468 func writeToDatabase(hash string, code string, workspaceName string, source int,
width, height int) { |
| 455 if db == nil { | 469 if db == nil { |
| 456 return | 470 return |
| 457 } | 471 } |
| 458 » if _, err := db.Exec("INSERT INTO webtry (code, hash, source_image_id) V
ALUES(?, ?, ?)", code, hash, source); err != nil { | 472 » if _, err := db.Exec("INSERT INTO webtry (code, hash, width, height, sou
rce_image_id) VALUES(?, ?, ?, ?, ?)", code, hash, width, height, source); err !=
nil { |
| 459 log.Printf("ERROR: Failed to insert code into database: %q\n", e
rr) | 473 log.Printf("ERROR: Failed to insert code into database: %q\n", e
rr) |
| 460 } | 474 } |
| 461 if workspaceName != "" { | 475 if workspaceName != "" { |
| 462 » » if _, err := db.Exec("INSERT INTO workspacetry (name, hash, sour
ce_image_id) VALUES(?, ?, ?)", workspaceName, hash, source); err != nil { | 476 » » if _, err := db.Exec("INSERT INTO workspacetry (name, hash, widt
h, height, source_image_id) VALUES(?, ?, ?, ?, ?)", workspaceName, hash, width,
height, source); err != nil { |
| 463 log.Printf("ERROR: Failed to insert into workspacetry ta
ble: %q\n", err) | 477 log.Printf("ERROR: Failed to insert into workspacetry ta
ble: %q\n", err) |
| 464 } | 478 } |
| 465 } | 479 } |
| 466 } | 480 } |
| 467 | 481 |
| 468 type Sources struct { | 482 type Sources struct { |
| 469 Id int `json:"id"` | 483 Id int `json:"id"` |
| 470 } | 484 } |
| 471 | 485 |
| 472 // sourcesHandler serves up the PNG of a specific try. | 486 // sourcesHandler serves up the PNG of a specific try. |
| (...skipping 119 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 592 w.Header().Set("Content-Type", "text/html") | 606 w.Header().Set("Content-Type", "text/html") |
| 593 if err := recentTemplate.Execute(w, Recent{Tries: recent, Titlebar: Titl
ebar{GitHash: gitHash, GitInfo: gitInfo}}); err != nil { | 607 if err := recentTemplate.Execute(w, Recent{Tries: recent, Titlebar: Titl
ebar{GitHash: gitHash, GitInfo: gitInfo}}); err != nil { |
| 594 log.Printf("ERROR: Failed to expand template: %q\n", err) | 608 log.Printf("ERROR: Failed to expand template: %q\n", err) |
| 595 } | 609 } |
| 596 } | 610 } |
| 597 | 611 |
| 598 type Workspace struct { | 612 type Workspace struct { |
| 599 Name string | 613 Name string |
| 600 Code string | 614 Code string |
| 601 Hash string | 615 Hash string |
| 616 Width int |
| 617 Height int |
| 602 Source int | 618 Source int |
| 603 Tries []Try | 619 Tries []Try |
| 604 Titlebar Titlebar | 620 Titlebar Titlebar |
| 605 } | 621 } |
| 606 | 622 |
| 607 // newWorkspace generates a new random workspace name and stores it in the datab
ase. | 623 // newWorkspace generates a new random workspace name and stores it in the datab
ase. |
| 608 func newWorkspace() (string, error) { | 624 func newWorkspace() (string, error) { |
| 609 for i := 0; i < 10; i++ { | 625 for i := 0; i < 10; i++ { |
| 610 adj := workspaceNameAdj[rand.Intn(len(workspaceNameAdj))] | 626 adj := workspaceNameAdj[rand.Intn(len(workspaceNameAdj))] |
| 611 noun := workspaceNameNoun[rand.Intn(len(workspaceNameNoun))] | 627 noun := workspaceNameNoun[rand.Intn(len(workspaceNameNoun))] |
| 612 suffix := rand.Intn(1000) | 628 suffix := rand.Intn(1000) |
| 613 name := fmt.Sprintf("%s-%s-%d", adj, noun, suffix) | 629 name := fmt.Sprintf("%s-%s-%d", adj, noun, suffix) |
| 614 if _, err := db.Exec("INSERT INTO workspace (name) VALUES(?)", n
ame); err == nil { | 630 if _, err := db.Exec("INSERT INTO workspace (name) VALUES(?)", n
ame); err == nil { |
| 615 return name, nil | 631 return name, nil |
| 616 } else { | 632 } else { |
| 617 log.Printf("ERROR: Failed to insert workspace into datab
ase: %q\n", err) | 633 log.Printf("ERROR: Failed to insert workspace into datab
ase: %q\n", err) |
| 618 } | 634 } |
| 619 } | 635 } |
| 620 return "", fmt.Errorf("Failed to create a new workspace") | 636 return "", fmt.Errorf("Failed to create a new workspace") |
| 621 } | 637 } |
| 622 | 638 |
| 623 // getCode returns the code for a given hash, or the empty string if not found. | 639 // getCode returns the code for a given hash, or the empty string if not found. |
| 624 func getCode(hash string) (string, int, error) { | 640 func getCode(hash string) (string, int, int, int, error) { |
| 625 code := "" | 641 code := "" |
| 642 width := 0 |
| 643 height := 0 |
| 626 source := 0 | 644 source := 0 |
| 627 » if err := db.QueryRow("SELECT code, source_image_id FROM webtry WHERE ha
sh=?", hash).Scan(&code, &source); err != nil { | 645 » if err := db.QueryRow("SELECT code, width, height, source_image_id FROM
webtry WHERE hash=?", hash).Scan(&code, &width, &height, &source); err != nil { |
| 628 log.Printf("ERROR: Code for hash is missing: %q\n", err) | 646 log.Printf("ERROR: Code for hash is missing: %q\n", err) |
| 629 » » return code, source, err | 647 » » return code, width, height, source, err |
| 630 } | 648 } |
| 631 » return code, source, nil | 649 » return code, width, height, source, nil |
| 632 } | 650 } |
| 633 | 651 |
| 634 func workspaceHandler(w http.ResponseWriter, r *http.Request) { | 652 func workspaceHandler(w http.ResponseWriter, r *http.Request) { |
| 635 log.Printf("Workspace Handler: %q\n", r.URL.Path) | 653 log.Printf("Workspace Handler: %q\n", r.URL.Path) |
| 636 if r.Method == "GET" { | 654 if r.Method == "GET" { |
| 637 tries := []Try{} | 655 tries := []Try{} |
| 638 match := workspaceLink.FindStringSubmatch(r.URL.Path) | 656 match := workspaceLink.FindStringSubmatch(r.URL.Path) |
| 639 name := "" | 657 name := "" |
| 640 if len(match) == 2 { | 658 if len(match) == 2 { |
| 641 name = match[1] | 659 name = match[1] |
| 642 rows, err := db.Query("SELECT create_ts, hash, source_im
age_id FROM workspacetry WHERE name=? ORDER BY create_ts", name) | 660 rows, err := db.Query("SELECT create_ts, hash, source_im
age_id FROM workspacetry WHERE name=? ORDER BY create_ts", name) |
| 643 if err != nil { | 661 if err != nil { |
| 644 reportError(w, r, err, "Failed to select.") | 662 reportError(w, r, err, "Failed to select.") |
| 645 return | 663 return |
| 646 } | 664 } |
| 647 for rows.Next() { | 665 for rows.Next() { |
| 648 var hash string | 666 var hash string |
| 649 var create_ts time.Time | 667 var create_ts time.Time |
| 650 var source int | 668 var source int |
| 651 if err := rows.Scan(&create_ts, &hash, &source);
err != nil { | 669 if err := rows.Scan(&create_ts, &hash, &source);
err != nil { |
| 652 log.Printf("Error: failed to fetch from
database: %q", err) | 670 log.Printf("Error: failed to fetch from
database: %q", err) |
| 653 continue | 671 continue |
| 654 } | 672 } |
| 655 tries = append(tries, Try{Hash: hash, Source: so
urce, CreateTS: create_ts.Format("2006-02-01")}) | 673 tries = append(tries, Try{Hash: hash, Source: so
urce, CreateTS: create_ts.Format("2006-02-01")}) |
| 656 } | 674 } |
| 657 } | 675 } |
| 658 var code string | 676 var code string |
| 659 var hash string | 677 var hash string |
| 678 var width int |
| 679 var height int |
| 660 source := 0 | 680 source := 0 |
| 661 if len(tries) == 0 { | 681 if len(tries) == 0 { |
| 662 code = DEFAULT_SAMPLE | 682 code = DEFAULT_SAMPLE |
| 683 width = 256 |
| 684 height = 256 |
| 663 } else { | 685 } else { |
| 664 hash = tries[len(tries)-1].Hash | 686 hash = tries[len(tries)-1].Hash |
| 665 » » » code, source, _ = getCode(hash) | 687 » » » code, width, height, source, _ = getCode(hash) |
| 666 } | 688 } |
| 667 w.Header().Set("Content-Type", "text/html") | 689 w.Header().Set("Content-Type", "text/html") |
| 668 » » if err := workspaceTemplate.Execute(w, Workspace{Tries: tries, C
ode: code, Name: name, Hash: hash, Source: source, Titlebar: Titlebar{GitHash: g
itHash, GitInfo: gitInfo}}); err != nil { | 690 » » if err := workspaceTemplate.Execute(w, Workspace{Tries: tries, C
ode: code, Name: name, Hash: hash, Width: width, Height: height, Source: source,
Titlebar: Titlebar{GitHash: gitHash, GitInfo: gitInfo}}); err != nil { |
| 669 log.Printf("ERROR: Failed to expand template: %q\n", err
) | 691 log.Printf("ERROR: Failed to expand template: %q\n", err
) |
| 670 } | 692 } |
| 671 } else if r.Method == "POST" { | 693 } else if r.Method == "POST" { |
| 672 name, err := newWorkspace() | 694 name, err := newWorkspace() |
| 673 if err != nil { | 695 if err != nil { |
| 674 http.Error(w, "Failed to create a new workspace.", 500) | 696 http.Error(w, "Failed to create a new workspace.", 500) |
| 675 return | 697 return |
| 676 } | 698 } |
| 677 http.Redirect(w, r, "/w/"+name, 302) | 699 http.Redirect(w, r, "/w/"+name, 302) |
| 678 } | 700 } |
| 679 } | 701 } |
| 680 | 702 |
| 681 // hasPreProcessor returns true if any line in the code begins with a # char. | 703 // hasPreProcessor returns true if any line in the code begins with a # char. |
| 682 func hasPreProcessor(code string) bool { | 704 func hasPreProcessor(code string) bool { |
| 683 lines := strings.Split(code, "\n") | 705 lines := strings.Split(code, "\n") |
| 684 for _, s := range lines { | 706 for _, s := range lines { |
| 685 if strings.HasPrefix(strings.TrimSpace(s), "#") { | 707 if strings.HasPrefix(strings.TrimSpace(s), "#") { |
| 686 return true | 708 return true |
| 687 } | 709 } |
| 688 } | 710 } |
| 689 return false | 711 return false |
| 690 } | 712 } |
| 691 | 713 |
| 692 type TryRequest struct { | 714 type TryRequest struct { |
| 693 Code string `json:"code"` | 715 Code string `json:"code"` |
| 716 Width int `json:"width"` |
| 717 Height int `json:"height"` |
| 694 Name string `json:"name"` // Optional name of the workspace the code
is in. | 718 Name string `json:"name"` // Optional name of the workspace the code
is in. |
| 695 Source int `json:"source"` // ID of the source image, 0 if none. | 719 Source int `json:"source"` // ID of the source image, 0 if none. |
| 696 } | 720 } |
| 697 | 721 |
| 698 // iframeHandler handles the GET and POST of the main page. | 722 // iframeHandler handles the GET and POST of the main page. |
| 699 func iframeHandler(w http.ResponseWriter, r *http.Request) { | 723 func iframeHandler(w http.ResponseWriter, r *http.Request) { |
| 700 log.Printf("IFrame Handler: %q\n", r.URL.Path) | 724 log.Printf("IFrame Handler: %q\n", r.URL.Path) |
| 701 if r.Method != "GET" { | 725 if r.Method != "GET" { |
| 702 http.NotFound(w, r) | 726 http.NotFound(w, r) |
| 703 return | 727 return |
| 704 } | 728 } |
| 705 match := iframeLink.FindStringSubmatch(r.URL.Path) | 729 match := iframeLink.FindStringSubmatch(r.URL.Path) |
| 706 if len(match) != 2 { | 730 if len(match) != 2 { |
| 707 http.NotFound(w, r) | 731 http.NotFound(w, r) |
| 708 return | 732 return |
| 709 } | 733 } |
| 710 hash := match[1] | 734 hash := match[1] |
| 711 if db == nil { | 735 if db == nil { |
| 712 http.NotFound(w, r) | 736 http.NotFound(w, r) |
| 713 return | 737 return |
| 714 } | 738 } |
| 715 var code string | 739 var code string |
| 716 » code, source, err := getCode(hash) | 740 » code, width, height, source, err := getCode(hash) |
| 717 if err != nil { | 741 if err != nil { |
| 718 http.NotFound(w, r) | 742 http.NotFound(w, r) |
| 719 return | 743 return |
| 720 } | 744 } |
| 721 // Expand the template. | 745 // Expand the template. |
| 722 w.Header().Set("Content-Type", "text/html") | 746 w.Header().Set("Content-Type", "text/html") |
| 723 » if err := iframeTemplate.Execute(w, userCode{Code: code, Hash: hash, Sou
rce: source}); err != nil { | 747 » if err := iframeTemplate.Execute(w, userCode{Code: code, Width: width, H
eight: height, Hash: hash, Source: source}); err != nil { |
| 724 log.Printf("ERROR: Failed to expand template: %q\n", err) | 748 log.Printf("ERROR: Failed to expand template: %q\n", err) |
| 725 } | 749 } |
| 726 } | 750 } |
| 727 | 751 |
| 728 type TryInfo struct { | 752 type TryInfo struct { |
| 729 Hash string `json:"hash"` | 753 Hash string `json:"hash"` |
| 730 Code string `json:"code"` | 754 Code string `json:"code"` |
| 755 Width int `json:"width"` |
| 756 Height int `json:"height"` |
| 731 Source int `json:"source"` | 757 Source int `json:"source"` |
| 732 } | 758 } |
| 733 | 759 |
| 734 // tryInfoHandler returns information about a specific try. | 760 // tryInfoHandler returns information about a specific try. |
| 735 func tryInfoHandler(w http.ResponseWriter, r *http.Request) { | 761 func tryInfoHandler(w http.ResponseWriter, r *http.Request) { |
| 736 log.Printf("Try Info Handler: %q\n", r.URL.Path) | 762 log.Printf("Try Info Handler: %q\n", r.URL.Path) |
| 737 if r.Method != "GET" { | 763 if r.Method != "GET" { |
| 738 http.NotFound(w, r) | 764 http.NotFound(w, r) |
| 739 return | 765 return |
| 740 } | 766 } |
| 741 match := tryInfoLink.FindStringSubmatch(r.URL.Path) | 767 match := tryInfoLink.FindStringSubmatch(r.URL.Path) |
| 742 if len(match) != 2 { | 768 if len(match) != 2 { |
| 743 http.NotFound(w, r) | 769 http.NotFound(w, r) |
| 744 return | 770 return |
| 745 } | 771 } |
| 746 hash := match[1] | 772 hash := match[1] |
| 747 » code, source, err := getCode(hash) | 773 » code, width, height, source, err := getCode(hash) |
| 748 if err != nil { | 774 if err != nil { |
| 749 http.NotFound(w, r) | 775 http.NotFound(w, r) |
| 750 return | 776 return |
| 751 } | 777 } |
| 752 m := TryInfo{ | 778 m := TryInfo{ |
| 753 Hash: hash, | 779 Hash: hash, |
| 754 Code: code, | 780 Code: code, |
| 781 Width: width, |
| 782 Height: height, |
| 755 Source: source, | 783 Source: source, |
| 756 } | 784 } |
| 757 resp, err := json.Marshal(m) | 785 resp, err := json.Marshal(m) |
| 758 if err != nil { | 786 if err != nil { |
| 759 reportError(w, r, err, "Failed to serialize a response.") | 787 reportError(w, r, err, "Failed to serialize a response.") |
| 760 return | 788 return |
| 761 } | 789 } |
| 762 w.Header().Set("Content-Type", "application/json") | 790 w.Header().Set("Content-Type", "application/json") |
| 763 w.Write(resp) | 791 w.Write(resp) |
| 764 } | 792 } |
| 765 | 793 |
| 766 func cleanCompileOutput(s, hash string) string { | 794 func cleanCompileOutput(s, hash string) string { |
| 767 old := "../../../cache/src/" + hash + ".cpp:" | 795 old := "../../../cache/src/" + hash + ".cpp:" |
| 768 log.Printf("INFO: replacing %q\n", old) | 796 log.Printf("INFO: replacing %q\n", old) |
| 769 return strings.Replace(s, old, "usercode.cpp:", -1) | 797 return strings.Replace(s, old, "usercode.cpp:", -1) |
| 770 } | 798 } |
| 771 | 799 |
| 772 // mainHandler handles the GET and POST of the main page. | 800 // mainHandler handles the GET and POST of the main page. |
| 773 func mainHandler(w http.ResponseWriter, r *http.Request) { | 801 func mainHandler(w http.ResponseWriter, r *http.Request) { |
| 774 log.Printf("Main Handler: %q\n", r.URL.Path) | 802 log.Printf("Main Handler: %q\n", r.URL.Path) |
| 775 requestsCounter.Inc(1) | 803 requestsCounter.Inc(1) |
| 776 if r.Method == "GET" { | 804 if r.Method == "GET" { |
| 777 code := DEFAULT_SAMPLE | 805 code := DEFAULT_SAMPLE |
| 778 source := 0 | 806 source := 0 |
| 807 width := 256 |
| 808 height := 256 |
| 779 match := directLink.FindStringSubmatch(r.URL.Path) | 809 match := directLink.FindStringSubmatch(r.URL.Path) |
| 780 var hash string | 810 var hash string |
| 781 if len(match) == 2 && r.URL.Path != "/" { | 811 if len(match) == 2 && r.URL.Path != "/" { |
| 782 hash = match[1] | 812 hash = match[1] |
| 783 if db == nil { | 813 if db == nil { |
| 784 http.NotFound(w, r) | 814 http.NotFound(w, r) |
| 785 return | 815 return |
| 786 } | 816 } |
| 787 // Update 'code' with the code found in the database. | 817 // Update 'code' with the code found in the database. |
| 788 » » » if err := db.QueryRow("SELECT code, source_image_id FROM
webtry WHERE hash=?", hash).Scan(&code, &source); err != nil { | 818 » » » if err := db.QueryRow("SELECT code, width, height, sourc
e_image_id FROM webtry WHERE hash=?", hash).Scan(&code, &width, &height, &source
); err != nil { |
| 789 http.NotFound(w, r) | 819 http.NotFound(w, r) |
| 790 return | 820 return |
| 791 } | 821 } |
| 792 } | 822 } |
| 793 // Expand the template. | 823 // Expand the template. |
| 794 w.Header().Set("Content-Type", "text/html") | 824 w.Header().Set("Content-Type", "text/html") |
| 795 » » if err := indexTemplate.Execute(w, userCode{Code: code, Hash: ha
sh, Source: source, Titlebar: Titlebar{GitHash: gitHash, GitInfo: gitInfo}}); er
r != nil { | 825 » » if err := indexTemplate.Execute(w, userCode{Code: code, Hash: ha
sh, Source: source, Width: width, Height: height, Titlebar: Titlebar{GitHash: gi
tHash, GitInfo: gitInfo}}); err != nil { |
| 796 log.Printf("ERROR: Failed to expand template: %q\n", err
) | 826 log.Printf("ERROR: Failed to expand template: %q\n", err
) |
| 797 } | 827 } |
| 798 } else if r.Method == "POST" { | 828 } else if r.Method == "POST" { |
| 799 w.Header().Set("Content-Type", "application/json") | 829 w.Header().Set("Content-Type", "application/json") |
| 800 buf := bytes.NewBuffer(make([]byte, 0, MAX_TRY_SIZE)) | 830 buf := bytes.NewBuffer(make([]byte, 0, MAX_TRY_SIZE)) |
| 801 n, err := buf.ReadFrom(r.Body) | 831 n, err := buf.ReadFrom(r.Body) |
| 802 if err != nil { | 832 if err != nil { |
| 803 reportTryError(w, r, err, "Failed to read a request body
.", "") | 833 reportTryError(w, r, err, "Failed to read a request body
.", "") |
| 804 return | 834 return |
| 805 } | 835 } |
| 806 if n == MAX_TRY_SIZE { | 836 if n == MAX_TRY_SIZE { |
| 807 err := fmt.Errorf("Code length equal to, or exceeded, %d
", MAX_TRY_SIZE) | 837 err := fmt.Errorf("Code length equal to, or exceeded, %d
", MAX_TRY_SIZE) |
| 808 reportTryError(w, r, err, "Code too large.", "") | 838 reportTryError(w, r, err, "Code too large.", "") |
| 809 return | 839 return |
| 810 } | 840 } |
| 811 request := TryRequest{} | 841 request := TryRequest{} |
| 812 if err := json.Unmarshal(buf.Bytes(), &request); err != nil { | 842 if err := json.Unmarshal(buf.Bytes(), &request); err != nil { |
| 813 reportTryError(w, r, err, "Coulnd't decode JSON.", "") | 843 reportTryError(w, r, err, "Coulnd't decode JSON.", "") |
| 814 return | 844 return |
| 815 } | 845 } |
| 816 if hasPreProcessor(request.Code) { | 846 if hasPreProcessor(request.Code) { |
| 817 err := fmt.Errorf("Found preprocessor macro in code.") | 847 err := fmt.Errorf("Found preprocessor macro in code.") |
| 818 reportTryError(w, r, err, "Preprocessor macros aren't al
lowed.", "") | 848 reportTryError(w, r, err, "Preprocessor macros aren't al
lowed.", "") |
| 819 return | 849 return |
| 820 } | 850 } |
| 821 » » hash, err := expandCode(LineNumbers(request.Code), request.Sourc
e) | 851 » » hash, err := expandCode(LineNumbers(request.Code), request.Sourc
e, request.Width, request.Height) |
| 822 if err != nil { | 852 if err != nil { |
| 823 reportTryError(w, r, err, "Failed to write the code to c
ompile.", hash) | 853 reportTryError(w, r, err, "Failed to write the code to c
ompile.", hash) |
| 824 return | 854 return |
| 825 } | 855 } |
| 826 » » writeToDatabase(hash, request.Code, request.Name, request.Source
) | 856 » » writeToDatabase(hash, request.Code, request.Name, request.Source
, request.Width, request.Height) |
| 827 err = expandGyp(hash) | 857 err = expandGyp(hash) |
| 828 if err != nil { | 858 if err != nil { |
| 829 reportTryError(w, r, err, "Failed to write the gyp file.
", hash) | 859 reportTryError(w, r, err, "Failed to write the gyp file.
", hash) |
| 830 return | 860 return |
| 831 } | 861 } |
| 832 » » cmd := "scripts/fiddle_wrapper " + hash | 862 » » cmd := fmt.Sprintf("scripts/fiddle_wrapper %s --width %d --heigh
t %d", hash, request.Width, request.Height) |
| 833 if *useChroot { | 863 if *useChroot { |
| 834 cmd = "schroot -c webtry --directory=/ -- /skia_build/sk
ia/experimental/webtry/" + cmd | 864 cmd = "schroot -c webtry --directory=/ -- /skia_build/sk
ia/experimental/webtry/" + cmd |
| 835 } | 865 } |
| 836 if request.Source > 0 { | 866 if request.Source > 0 { |
| 837 » » » cmd += fmt.Sprintf(" image-%d.png", request.Source) | 867 » » » cmd += fmt.Sprintf(" --source image-%d.png", request.Sou
rce) |
| 838 } | 868 } |
| 839 | 869 |
| 840 message, err := doCmd(cmd) | 870 message, err := doCmd(cmd) |
| 841 if err != nil { | 871 if err != nil { |
| 842 reportTryError(w, r, err, "Failed to run the code:\n"+me
ssage, hash) | 872 reportTryError(w, r, err, "Failed to run the code:\n"+me
ssage, hash) |
| 843 return | 873 return |
| 844 } | 874 } |
| 845 png, err := ioutil.ReadFile("../../../inout/" + hash + ".png") | 875 png, err := ioutil.ReadFile("../../../inout/" + hash + ".png") |
| 846 if err != nil { | 876 if err != nil { |
| 847 reportTryError(w, r, err, "Failed to open the generated
PNG.", hash) | 877 reportTryError(w, r, err, "Failed to open the generated
PNG.", hash) |
| (...skipping 25 matching lines...) Expand all Loading... |
| 873 http.HandleFunc("/sources/", autogzip.HandleFunc(sourcesHandler)) | 903 http.HandleFunc("/sources/", autogzip.HandleFunc(sourcesHandler)) |
| 874 | 904 |
| 875 // Resources are served directly | 905 // Resources are served directly |
| 876 // TODO add support for caching/etags/gzip | 906 // TODO add support for caching/etags/gzip |
| 877 http.Handle("/res/", autogzip.Handle(http.FileServer(http.Dir("./")))) | 907 http.Handle("/res/", autogzip.Handle(http.FileServer(http.Dir("./")))) |
| 878 | 908 |
| 879 // TODO Break out /c/ as it's own handler. | 909 // TODO Break out /c/ as it's own handler. |
| 880 http.HandleFunc("/", autogzip.HandleFunc(mainHandler)) | 910 http.HandleFunc("/", autogzip.HandleFunc(mainHandler)) |
| 881 log.Fatal(http.ListenAndServe(*port, nil)) | 911 log.Fatal(http.ListenAndServe(*port, nil)) |
| 882 } | 912 } |
| OLD | NEW |