1core.go 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976
  1. package main
  2. import (
  3. "database/sql"
  4. _ "github.com/go-sql-driver/mysql"
  5. // "fmt"
  6. "html"
  7. "html/template"
  8. "log"
  9. "net/http"
  10. "net/url"
  11. "strconv"
  12. "strings"
  13. "unicode/utf8"
  14. // "time"
  15. )
  16. type indexPage struct{}
  17. type errorReport struct{ Error string }
  18. type surpriseURL struct{ Url string }
  19. type settingsPage struct{ Worksafe, FilterHTTPS bool }
  20. type MySQLResults struct{ Id, Url, Title, Description, Body string }
  21. type PageData struct {
  22. DBResults []MySQLResults
  23. Query, Page string
  24. FindMore bool
  25. }
  26. func main() {
  27. http.HandleFunc("/", handler)
  28. http.HandleFunc("/json", handler)
  29. http.HandleFunc("/json/", handler)
  30. http.HandleFunc("/surprise", surprise)
  31. http.HandleFunc("/surprise/", surprise)
  32. http.HandleFunc("/settings/", settings)
  33. http.HandleFunc("/settings", settings)
  34. log.Fatal(http.ListenAndServe("localhost:8080", nil))
  35. }
  36. //https://golang.org/pkg/net/http/#Request
  37. func handler(w http.ResponseWriter, r *http.Request) {
  38. //fmt.Fprintf(w, "%s %s \n", r.Method, r.URL)
  39. //fmt.Fprintf(w, "%s \n", r.URL.RawQuery)
  40. //check if worksafe+https cookie enabled.
  41. filterHTTPS := false
  42. worksafe := true
  43. worksafeHTTPSCookie, err := r.Cookie("ws")
  44. if err != nil {
  45. worksafe = true
  46. filterHTTPS = false
  47. } else if worksafeHTTPSCookie.Value == "0" {
  48. worksafe = false
  49. filterHTTPS = false
  50. } else if worksafeHTTPSCookie.Value == "1" {
  51. worksafe = true
  52. filterHTTPS = false
  53. } else if worksafeHTTPSCookie.Value == "2" {
  54. worksafe = false
  55. filterHTTPS = true
  56. } else if worksafeHTTPSCookie.Value == "3" {
  57. worksafe = true
  58. filterHTTPS = true
  59. }
  60. //setup for error report
  61. error := errorReport{}
  62. //Get the raw query
  63. m, _ := url.ParseQuery(r.URL.RawQuery)
  64. //Get the query parameters (q and o)
  65. //fmt.Fprintf(w,"%s\n%s\n", m["q"][0], m["o"][0])
  66. json := false
  67. if strings.Contains(r.URL.Path, "/json") {
  68. json = true
  69. if _, ok := m["nsfw"]; ok { //check if &nsfw added to json url
  70. worksafe = false
  71. }
  72. }
  73. query := ""
  74. queryNoQuotes := ""
  75. queryNoQuotes_SQLsafe := ""
  76. offset := "0"
  77. page := "0"
  78. //Check if query and page params exist
  79. if _, ok := m["q"]; ok {
  80. query = strings.Replace(m["q"][0], "'", "''", -1)
  81. queryNoQuotes = m["q"][0]
  82. }
  83. if _, ok := m["p"]; ok {//gets page num, will convert to offset further down
  84. page = strings.Replace(m["p"][0], "'", "''", -1)
  85. offset = page
  86. }
  87. lim := "12"
  88. if query == "" { //what do if no query found?
  89. //load index if no query detected
  90. if r.URL.Path == "/" {
  91. p := indexPage{}
  92. t, _ := template.ParseFiles("coreassets/form.html.go")
  93. t.Execute(w, p)
  94. } else if strings.Contains(r.URL.Path, "/json") { //load json info page if json selected
  95. p := indexPage{}
  96. t, _ := template.ParseFiles("coreassets/json/json.html.go")
  97. t.Execute(w, p)
  98. } else {
  99. p := indexPage{}
  100. t, _ := template.ParseFiles("coreassets/form.html.go")
  101. t.Execute(w, p)
  102. }
  103. } else {
  104. //Make sure offset is a number
  105. offsetInt, err := strconv.Atoi(offset)
  106. if err != nil {
  107. offset = "0"
  108. offsetInt = 0
  109. }
  110. //Make sure page is a number
  111. pageInt, err := strconv.Atoi(page)
  112. if err != nil {
  113. page = "0"
  114. pageInt = 0
  115. }
  116. //Convert lim to number
  117. limInt, _ := strconv.Atoi(lim)
  118. //convert page num to offset
  119. if offsetInt > 0 {
  120. offsetInt --;
  121. }
  122. offsetInt = offsetInt * limInt
  123. offset = strconv.Itoa(offsetInt)
  124. //get some details from the raw query
  125. var additions string
  126. querylen := len(query)
  127. //see if a search redirect (! or &) is used for a different search engine
  128. if json == false && (strings.Contains(m["q"][0],"!") || strings.Contains(m["q"][0],"&")){
  129. searchredirect(w, r, m["q"][0])
  130. }
  131. //phone users
  132. if query[querylen-1] == ' '{
  133. query = query[:querylen-1]
  134. queryNoQuotes = queryNoQuotes[:len(queryNoQuotes)-1]
  135. querylen = len(query)
  136. }
  137. //check if user wants to limit search to a specific website
  138. sitePos := -1
  139. siteEnd := 0
  140. siteURL := ""
  141. if strings.Index(strings.ToLower(query), "site:") > -1 {
  142. //get url user wants to search and remove it from the query stringre
  143. sitePos = strings.Index(strings.ToLower(query), "site:")
  144. siteEnd = strings.Index(query[sitePos:], " ")
  145. //fmt.Printf("\n%d\n%d\n",sitePos,siteEnd)
  146. if siteEnd > -1 && sitePos > 1 { //site is not last part of query
  147. siteURL = query[sitePos+5 : siteEnd+sitePos]
  148. query = query[:sitePos-1] + query[siteEnd+sitePos:]
  149. queryNoQuotes = queryNoQuotes[:sitePos-1] + queryNoQuotes[siteEnd+sitePos:]
  150. additions = additions + "AND url LIKE '%" + siteURL + "%' "
  151. } else if siteEnd > -1 && sitePos == 0 { //site is at beginning
  152. siteURL = query[sitePos+5 : siteEnd]
  153. query = query[siteEnd+1:]
  154. queryNoQuotes = queryNoQuotes[siteEnd+1:]
  155. additions = additions + "AND url LIKE '%" + siteURL + "%' "
  156. } else if siteEnd < 0 && sitePos > 1 { //site is at end
  157. siteURL = query[sitePos+5:]
  158. query = query[:sitePos-1]
  159. queryNoQuotes = queryNoQuotes[:sitePos-1]
  160. additions = additions + "AND url LIKE '%" + siteURL + "%' "
  161. }else if querylen > 5{
  162. query = query[5:]
  163. }
  164. querylen = len(query)
  165. }
  166. //fmt.Printf("Addition: \n%s\nQuery: '%s'\n",additions,query)
  167. //see if user uses -https flag (instead of cookie settings option)
  168. if querylen > 7 && strings.ToLower(query[querylen-7:querylen]) == " -https" {
  169. filterHTTPS = true
  170. query = query[0 : querylen-7]
  171. querylen = len(query)
  172. }
  173. //check if user wants to search within a time window (day,week,month)
  174. option := ""
  175. //fmt.Printf("\n'%s'\n",query)
  176. location := strings.Index(query, " !")
  177. if location == -1 {
  178. location = strings.Index(query, " &")
  179. }
  180. if location > -1 && strings.Index(query[location+1:querylen], " ") == -1 { //option is at end of query
  181. option = query[location+2 : querylen]
  182. query = query[:location]
  183. queryNoQuotes = queryNoQuotes[:location]
  184. querylen = len(query)
  185. }else if querylen > 0 && (query[0] == '!' || query[0] == '&') && strings.Index(query, " ") > -1{ //option is at start of query
  186. option = query[1:strings.Index(query, " ")]
  187. query = query[strings.Index(query, " ")+1:]
  188. queryNoQuotes = queryNoQuotes[strings.Index(queryNoQuotes, " ")+1:]
  189. querylen = len(query)
  190. }
  191. option = strings.ToLower(option)
  192. if option != "" {
  193. if option == "td" { //day
  194. additions = additions + "AND date > NOW() - INTERVAL 1 DAY "
  195. } else if option == "tw" { //week
  196. additions = additions + "AND date > NOW() - INTERVAL 7 DAY "
  197. } else if option == "tm" { //month
  198. additions = additions + "AND date > NOW() - INTERVAL 30 DAY "
  199. } else if option == "ty" { //year
  200. additions = additions + "AND date > NOW() - INTERVAL 365 DAY "
  201. }
  202. }
  203. //check if worksafe and filterHTTPS flags set
  204. if worksafe == true {
  205. additions = additions + "AND worksafe = '1' "
  206. }
  207. if filterHTTPS == true {
  208. additions = additions + "AND http = '1' "
  209. }
  210. //if query is just 1 or 2 letters, help make it work. Also CIA :D
  211. if len(query) < 3 || query == "cia" || query == "CIA" {
  212. queryfix := " " + query + " *"
  213. query = queryfix
  214. queryNoQuotes = queryfix
  215. }
  216. if query == "c++" || query == "C++" { //shitty but works for now
  217. query = "c++ programming"
  218. }
  219. //search if query has quotes and remove them (so we can find the longest word in the query)
  220. exactMatch := false
  221. //queryNoQuotes := query
  222. if strings.Contains(query, "\"") {
  223. exactMatch = true
  224. queryNoQuotes = strings.TrimLeft(queryNoQuotes, "\"")
  225. getlastquote := strings.Split(queryNoQuotes, "\"")
  226. queryNoQuotes = getlastquote[0]
  227. //fmt.Printf("%s \n", queryNoQuotes)
  228. }
  229. //Prepare to find longest word in query
  230. words := strings.Split(queryNoQuotes, " ")
  231. longestWordLength := 0
  232. longestWord := ""
  233. wordcount := 0
  234. longestwordelementnum := 0
  235. queryNoQuotesOrFlags := ""
  236. requiredword := ""
  237. flags := ""
  238. //queryNoFlags := ""
  239. //first remove any flags inside var queryNoQuotes, also grab any required words (+ prefix)
  240. if strings.Contains(queryNoQuotes, "-") || strings.Contains(queryNoQuotes, "+") {
  241. for i, wordNoFlags := range words {
  242. if i > 0 && strings.HasPrefix(wordNoFlags, "-") == false && strings.HasPrefix(wordNoFlags, "+") == false { //add a space after
  243. queryNoQuotesOrFlags += " "
  244. }
  245. if strings.HasPrefix(wordNoFlags, "-") == false && strings.HasPrefix(wordNoFlags, "+") == false {
  246. queryNoQuotesOrFlags += wordNoFlags
  247. }
  248. if strings.HasPrefix(wordNoFlags, "+") == true && len(wordNoFlags) > 1 { //get requiredword
  249. requiredword = wordNoFlags[1:len(wordNoFlags)]
  250. }
  251. if i > 0 && strings.HasPrefix(wordNoFlags, "-") == true || strings.HasPrefix(wordNoFlags, "+") == true {
  252. flags += " " + wordNoFlags
  253. }
  254. }
  255. queryNoQuotes = queryNoQuotesOrFlags
  256. }
  257. //now find longest word
  258. words = strings.Split(queryNoQuotes, " ")
  259. if exactMatch == false {
  260. for _, word := range words {
  261. if len(word) > longestWordLength {
  262. longestWordLength = len(word)
  263. longestWord = word
  264. longestwordelementnum = wordcount
  265. }
  266. wordcount++
  267. }
  268. }
  269. //remove the '*' if contained anywhere in queryNoQuotes
  270. if strings.Contains(queryNoQuotes, "*") && exactMatch == false {
  271. queryNoQuotes = strings.Replace(queryNoQuotes, "*", "", -1)
  272. }
  273. //get sql safe querynoquotes and flags
  274. queryNoQuotes_SQLsafe = strings.Replace(queryNoQuotes, "'", "''", -1)
  275. flags = strings.Replace(flags, "'", "''", -1)
  276. //fmt.Printf("\nquery: %s\nquerynoquotes: %s\nquerynoquotes_sqlsafe: %s\n",query,queryNoQuotes,queryNoQuotes_SQLsafe)
  277. //fmt.Fprintf(w,"%s\n%s\n", query,offset)
  278. //fmt.Printf("hai\n")
  279. //get copy of original query because we might have to modify it further
  280. queryOriginal := query
  281. tRes := MySQLResults{}
  282. var res = PageData{}
  283. //init the db and set charset
  284. db, err := sql.Open("mysql", "guest:qwer@/wiby?charset=utf8mb4")
  285. if err != nil {
  286. p := indexPage{}
  287. t, _ := template.ParseFiles("coreassets/error.html.go")
  288. t.Execute(w, p)
  289. }
  290. defer db.Close()
  291. // Open doesn't open a connection. Validate DSN data:
  292. err = db.Ping()
  293. if err != nil {
  294. error.Error = err.Error()
  295. t, _ := template.ParseFiles("coreassets/error.html.go")
  296. t.Execute(w, error)
  297. }
  298. //Check if query is a url.
  299. urlDetected := false
  300. isURL := ""
  301. if strings.Index(query, " ") == -1 && strings.Index(query, "\"") == -1 && strings.Index(query, ".") > -1 { //note this will also flag on file extensions
  302. if len(query) > 6 && (query[0:7] == "http://" || query[0:7] == "HTTP://") {
  303. query = query[7:]
  304. } else if len(query) > 7 && (query[0:8] == "https://" || query[0:8] == "HTTPS://") {
  305. query = query[8:]
  306. }
  307. if len(queryNoQuotes_SQLsafe) > 6 && (queryNoQuotes_SQLsafe[0:7] == "http://" || queryNoQuotes_SQLsafe[0:7] == "HTTP://") {
  308. queryNoQuotes_SQLsafe = queryNoQuotes_SQLsafe[7:]
  309. } else if len(queryNoQuotes_SQLsafe) > 7 && (queryNoQuotes_SQLsafe[0:8] == "https://" || queryNoQuotes_SQLsafe[0:8] == "HTTPS://") {
  310. queryNoQuotes_SQLsafe = queryNoQuotes_SQLsafe[8:]
  311. }
  312. query = "\"" + query + "\""
  313. urlDetected = true
  314. isURL = "WHEN MATCH(url) AGAINST('\"" + queryNoQuotes_SQLsafe + "\"' IN BOOLEAN MODE) THEN 25"
  315. }
  316. //Check if query contains a hyphenated word. Will wrap quotes around hyphenated words that aren't part of a string which is already wraped in quotes.
  317. if (strings.Contains(queryNoQuotes_SQLsafe, "-") || strings.Contains(queryNoQuotes_SQLsafe, "+")) && urlDetected == false {
  318. hyphenwords := strings.Split(query, " ")
  319. query = ""
  320. quotes := 0
  321. for i, word := range hyphenwords {
  322. if strings.Contains(word, "\"") {
  323. quotes++
  324. }
  325. if ((strings.Contains(word, "-") && word[0] != '-') || (strings.Contains(word, "+") && word[0] != '+')) && quotes%2 == 0 { //if hyphen or plus exists, not a flag, not wrapped in quotes already
  326. word = "\"" + word + "\""
  327. }
  328. if i > 0 {
  329. query += " "
  330. }
  331. query += word
  332. }
  333. }
  334. //fmt.Printf(">%s<\n", query)
  335. queryWithQuotesAndFlags := "\"" + queryNoQuotes_SQLsafe + "\"" + flags
  336. //perform full text search FOR InnoDB STORAGE ENGINE or MyISAM
  337. var sqlQuery, id, url, title, description, body string
  338. if(exactMatch==false && urlDetected==false && strings.Index(query, " ") != -1){
  339. sqlQuery = "SELECT id, url, title, description, body FROM windex WHERE enable = '1' " + additions + "ORDER BY CASE WHEN MATCH(tags) AGAINST('" + queryWithQuotesAndFlags + "' IN BOOLEAN MODE) THEN 30 " + isURL + " WHEN MATCH(title) AGAINST('" + queryWithQuotesAndFlags + "' IN BOOLEAN MODE) AND Match(title) AGAINST('" + query + "' IN BOOLEAN MODE) THEN 20 WHEN MATCH(body) AGAINST('" + queryWithQuotesAndFlags + "' IN BOOLEAN MODE) THEN 19 WHEN MATCH(title) AGAINST('" + queryWithQuotesAndFlags + "' IN BOOLEAN MODE) THEN 16 WHEN MATCH(description) AGAINST('" + queryWithQuotesAndFlags + "' IN BOOLEAN MODE) THEN 15 WHEN Match(title) AGAINST('" + query + "' IN BOOLEAN MODE) THEN Match(title) AGAINST('" + query + "' IN BOOLEAN MODE) WHEN MATCH(body) AGAINST('" + query + "' IN BOOLEAN MODE) THEN 1 WHEN MATCH(url) AGAINST('" + query + "' IN BOOLEAN MODE) THEN 0 END DESC, id DESC LIMIT " + lim + " OFFSET " + offset + ""
  340. }else{
  341. sqlQuery = "SELECT id, url, title, description, body FROM windex WHERE MATCH(tags, body, description, title, url) AGAINST('" + queryWithQuotesAndFlags + "' IN BOOLEAN MODE) AND enable = '1' " + additions + "ORDER BY CASE WHEN MATCH(tags) AGAINST('" + queryWithQuotesAndFlags + "' IN BOOLEAN MODE) THEN 30 " + isURL + " WHEN MATCH(title) AGAINST('" + queryWithQuotesAndFlags + "' IN BOOLEAN MODE) THEN 20 END DESC, id DESC LIMIT " + lim + " OFFSET " + offset + ""
  342. }
  343. rows, err := db.Query(sqlQuery)
  344. // fmt.Printf("\n%s\n",sqlQuery)
  345. if err != nil {
  346. res.Page = strconv.Itoa(0)
  347. res.Query = m["q"][0] //get original unsafe query
  348. if json {
  349. w.Header().Set("Content-Type", "application/json")
  350. t, _ := template.ParseFiles("coreassets/json/results.json.go")
  351. t.Execute(w, res)
  352. } else {
  353. t, _ := template.ParseFiles("coreassets/results.html.go")
  354. t.Execute(w, res)
  355. }
  356. //p := indexPage{}
  357. //t, _ := template.ParseFiles("coreassets/form.html.go")
  358. //t.Execute(w, p)
  359. return
  360. }
  361. if urlDetected == true {
  362. query = queryOriginal
  363. }
  364. count := 0
  365. for rows.Next() {
  366. count++
  367. //this will get set if position of longest word of query is found within body
  368. pos := -1
  369. err := rows.Scan(&id, &url, &title, &description, &body)
  370. if err != nil {
  371. error.Error = err.Error()
  372. t, _ := template.ParseFiles("coreassets/error.html.go")
  373. t.Execute(w, error)
  374. }
  375. //find query inside body of page
  376. if exactMatch == false {
  377. /* //remove the '*' if contained anywhere in query
  378. if strings.Contains(queryNoQuotes,"*"){
  379. queryNoQuotes = strings.Replace(queryNoQuotes, "*", "", -1)
  380. } */
  381. if len(requiredword) > 0 { //search for position of required word if any, else search for position of whole query
  382. pos = strings.Index(strings.ToLower(body), strings.ToLower(requiredword))
  383. } else if pos == -1 {
  384. pos = strings.Index(strings.ToLower(body), strings.ToLower(queryNoQuotes))
  385. }
  386. if pos == -1 { //prepare to find position of longest query word (or required word) within body
  387. //remove the '*' at the end of the longest word if present
  388. if strings.Contains(longestWord, "*") {
  389. longestWord = strings.Replace(longestWord, "*", "", -1)
  390. }
  391. //search within body for position of longest query word.
  392. pos = strings.Index(strings.ToLower(body), strings.ToLower(longestWord))
  393. //not found?, set position to a different word, make sure there's no wildcard on it
  394. if pos == -1 && wordcount > 1 {
  395. if longestwordelementnum > 0 {
  396. words[0] = strings.Replace(words[0], "*", "", -1)
  397. pos = strings.Index(strings.ToLower(body), strings.ToLower(words[0]))
  398. }
  399. if longestwordelementnum == 0 {
  400. words[1] = strings.Replace(words[1], "*", "", -1)
  401. pos = strings.Index(strings.ToLower(body), strings.ToLower(words[1]))
  402. }
  403. }
  404. }
  405. } else { //if exact match, find position of query within body
  406. pos = strings.Index(strings.ToLower(body), strings.ToLower(queryNoQuotes))
  407. }
  408. //still not found?, set position to 0
  409. if pos == -1 {
  410. pos = 0
  411. }
  412. //Adjust position for runes within body
  413. pos = utf8.RuneCountInString(body[:pos])
  414. starttext := 0
  415. //ballpark := 0
  416. ballparktext := ""
  417. //figure out how much preceding text to use
  418. if pos < 32 {
  419. starttext = 0
  420. } else if pos > 25 {
  421. starttext = pos - 25
  422. } else if pos > 20 {
  423. starttext = pos - 15
  424. }
  425. //total length of the ballpark
  426. textlength := 180
  427. //populate the ballpark
  428. if pos >= 0 {
  429. ballparktext = substr(body, starttext, starttext+textlength)
  430. } //else{ ballpark = 0}//looks unused
  431. //find position of nearest Period
  432. //foundPeriod := true
  433. posPeriod := strings.Index(ballparktext, ". ") + starttext + 1
  434. //find position of nearest Space
  435. //foundSpace := true
  436. posSpace := strings.Index(ballparktext, " ") + starttext
  437. //if longest word in query is after a period+space within ballpark, reset starttext to that point
  438. if (pos - starttext) > posPeriod {
  439. starttext = posPeriod
  440. //populate the bodymatch
  441. if (pos - starttext) >= 0 {
  442. body = substr(body, starttext, starttext+textlength)
  443. } else {
  444. body = ""
  445. }
  446. } else if pos > posSpace { //else if longest word in query is after a space within ballpark, reset starttext to that point
  447. //else if(pos-starttext) > posSpace//else if longest word in query is after a space within ballpark, reset starttext to that point
  448. starttext = posSpace
  449. //populate the bodymatch
  450. if (pos - starttext) >= 0 {
  451. body = substr(body, starttext, starttext+textlength)
  452. } else {
  453. body = ""
  454. }
  455. } else //else just set the bodymatch to the ballparktext
  456. {
  457. //populate the bodymatch
  458. if (pos - starttext) >= 0 {
  459. body = ballparktext
  460. } else {
  461. body = ""
  462. }
  463. }
  464. tRes.Id = id
  465. tRes.Url = url
  466. tRes.Title = html.UnescapeString(title)
  467. tRes.Description = html.UnescapeString(description)
  468. tRes.Body = html.UnescapeString(body)
  469. if json == true {
  470. tRes.Title = JSONRealEscapeString(tRes.Title)
  471. tRes.Description = JSONRealEscapeString(tRes.Description)
  472. tRes.Body = JSONRealEscapeString(tRes.Body)
  473. }
  474. res.DBResults = append(res.DBResults, tRes)
  475. }
  476. defer rows.Close()
  477. rows.Close()
  478. //================================================================================================================================
  479. //no results found (count==0), so do a wildcard search (repeat the above process)
  480. addWildcard := false
  481. if count == 0 && offset == "0" && urlDetected == false && exactMatch == false {
  482. addWildcard = true
  483. query = strings.Replace(query, "\"", "", -1) //remove some things innodb gets fussy over
  484. query = strings.Replace(query, "*", "", -1)
  485. query = strings.Replace(query, "'", "", -1)
  486. queryNoQuotes_SQLsafe = strings.Replace(queryNoQuotes_SQLsafe, "\"", "", -1)
  487. queryNoQuotes_SQLsafe = strings.Replace(queryNoQuotes_SQLsafe, "*", "", -1)
  488. queryNoQuotes_SQLsafe = strings.Replace(queryNoQuotes_SQLsafe, "'", "", -1)
  489. query = query + "*"
  490. sqlQuery = "SELECT id, url, title, description, body FROM windex WHERE Match(tags, body, description, title, url) Against('" + query + "' IN BOOLEAN MODE) AND enable = '1' " + additions + "ORDER BY CASE WHEN MATCH(tags) AGAINST('" + queryWithQuotesAndFlags + "' IN BOOLEAN MODE) THEN 30 END DESC, id DESC LIMIT " + lim + " OFFSET " + offset + ""
  491. rows2, err := db.Query(sqlQuery)
  492. if err != nil {
  493. res.Page = strconv.Itoa(0)
  494. res.Query = m["q"][0] //get original unsafe query
  495. if json {
  496. w.Header().Set("Content-Type", "application/json")
  497. t, _ := template.ParseFiles("coreassets/json/results.json.go")
  498. t.Execute(w, res)
  499. } else {
  500. t, _ := template.ParseFiles("coreassets/results.html.go")
  501. t.Execute(w, res)
  502. }
  503. //p := indexPage{}
  504. //t, _ := template.ParseFiles("coreassets/form.html.go")
  505. //t.Execute(w, p)
  506. return
  507. }
  508. for rows2.Next() {
  509. count++
  510. //this will get set if position of longest word of query is found within body
  511. pos := -1
  512. err := rows2.Scan(&id, &url, &title, &description, &body)
  513. if err != nil {
  514. error.Error = err.Error()
  515. t, _ := template.ParseFiles("coreassets/error.html.go")
  516. t.Execute(w, error)
  517. }
  518. //find query inside body of page
  519. if exactMatch == false {
  520. //remove the '*' if contained anywhere in query
  521. /*if strings.Contains(queryNoQuotes,"*"){
  522. queryNoQuotes = strings.Replace(queryNoQuotes, "*", "", -1)
  523. }*/
  524. if len(requiredword) > 0 { //search for position of required word if any, else search for position of whole query
  525. pos = strings.Index(strings.ToLower(body), strings.ToLower(requiredword))
  526. } else if pos == -1 {
  527. pos = strings.Index(strings.ToLower(body), strings.ToLower(queryNoQuotes))
  528. }
  529. if pos == -1 { //Not found? prepare to find position of longest query word within body
  530. //remove the '*' at the end of the longest word if present
  531. if strings.Contains(longestWord, "*") {
  532. longestWord = strings.Replace(longestWord, "*", "", -1)
  533. }
  534. //search within body for position of longest query word.
  535. pos = strings.Index(strings.ToLower(body), strings.ToLower(longestWord))
  536. //not found?, set position to a different word, make sure there's no wildcard on it
  537. if pos == -1 && wordcount > 1 {
  538. if longestwordelementnum > 0 {
  539. words[0] = strings.Replace(words[0], "*", "", -1)
  540. pos = strings.Index(strings.ToLower(body), strings.ToLower(words[0]))
  541. }
  542. if longestwordelementnum == 0 {
  543. words[1] = strings.Replace(words[1], "*", "", -1)
  544. pos = strings.Index(strings.ToLower(body), strings.ToLower(words[1]))
  545. }
  546. }
  547. }
  548. } else { //if exact match, find position of query within body
  549. pos = strings.Index(strings.ToLower(body), strings.ToLower(queryNoQuotes))
  550. }
  551. //still not found?, set position to 0
  552. if pos == -1 {
  553. pos = 0
  554. }
  555. //Adjust position for runes within body
  556. pos = utf8.RuneCountInString(body[:pos])
  557. starttext := 0
  558. //ballpark := 0
  559. ballparktext := ""
  560. //figure out how much preceding text to use
  561. if pos < 32 {
  562. starttext = 0
  563. } else if pos > 25 {
  564. starttext = pos - 25
  565. } else if pos > 20 {
  566. starttext = pos - 15
  567. }
  568. //total length of the ballpark
  569. textlength := 180
  570. //populate the ballpark
  571. if pos >= 0 {
  572. ballparktext = substr(body, starttext, starttext+textlength)
  573. } //else{ ballpark = 0}//looks unused
  574. //find position of nearest Period
  575. //foundPeriod := true
  576. posPeriod := strings.Index(ballparktext, ". ") + starttext + 1
  577. //find position of nearest Space
  578. //foundSpace := true
  579. posSpace := strings.Index(ballparktext, " ") + starttext
  580. //if longest word in query is after a period+space within ballpark, reset starttext to that point
  581. if (pos - starttext) > posPeriod {
  582. starttext = posPeriod
  583. //populate the bodymatch
  584. if (pos - starttext) >= 0 {
  585. body = substr(body, starttext, starttext+textlength)
  586. } else {
  587. body = ""
  588. }
  589. } else if pos > posSpace { //else if longest word in query is after a space within ballpark, reset starttext to that point
  590. //else if(pos-starttext) > posSpace//else if longest word in query is after a space within ballpark, reset starttext to that point
  591. starttext = posSpace
  592. //populate the bodymatch
  593. if (pos - starttext) >= 0 {
  594. body = substr(body, starttext, starttext+textlength)
  595. } else {
  596. body = ""
  597. }
  598. } else //else just set the bodymatch to the ballparktext
  599. {
  600. //populate the bodymatch
  601. if (pos - starttext) >= 0 {
  602. body = ballparktext
  603. } else {
  604. body = ""
  605. }
  606. }
  607. tRes.Id = id
  608. tRes.Url = url
  609. tRes.Title = html.UnescapeString(title)
  610. tRes.Description = html.UnescapeString(description)
  611. tRes.Body = html.UnescapeString(body)
  612. if json == true {
  613. tRes.Title = JSONRealEscapeString(tRes.Title)
  614. tRes.Description = JSONRealEscapeString(tRes.Description)
  615. tRes.Body = JSONRealEscapeString(tRes.Body)
  616. }
  617. res.DBResults = append(res.DBResults, tRes)
  618. }
  619. defer rows2.Close()
  620. rows2.Close()
  621. }
  622. //=======================================================================================================================
  623. //http://go-database-sql.org/retrieving.html
  624. //Close DB
  625. db.Close()
  626. //If results = lim, allow the find more link
  627. if count >= limInt && addWildcard == false{
  628. res.FindMore = true
  629. } else {
  630. res.FindMore = false
  631. }
  632. if(pageInt == 0){
  633. pageInt+=2
  634. }else{
  635. pageInt++;
  636. }
  637. res.Page = strconv.Itoa(pageInt)
  638. res.Query = m["q"][0] //get original unsafe query
  639. if json {
  640. w.Header().Set("Content-Type", "application/json")
  641. t, _ := template.ParseFiles("coreassets/json/results.json.go")
  642. t.Execute(w, res)
  643. } else {
  644. t, _ := template.ParseFiles("coreassets/results.html.go")
  645. t.Execute(w, res)
  646. }
  647. }
  648. }
  649. func settings(w http.ResponseWriter, r *http.Request) {
  650. //setup for error report
  651. error := errorReport{}
  652. //check if worksafe (adult content) cookie enabled.
  653. filterHTTPS := false
  654. worksafe := true
  655. worksafewasoff := false
  656. worksafeHTTPSCookie, err := r.Cookie("ws")
  657. if err != nil {
  658. worksafe = true
  659. filterHTTPS = false
  660. } else if worksafeHTTPSCookie.Value == "0" {
  661. worksafe = false
  662. filterHTTPS = false
  663. worksafewasoff = true
  664. } else if worksafeHTTPSCookie.Value == "1" {
  665. worksafe = true
  666. filterHTTPS = false
  667. } else if worksafeHTTPSCookie.Value == "2" {
  668. worksafe = false
  669. filterHTTPS = true
  670. worksafewasoff = true
  671. } else if worksafeHTTPSCookie.Value == "3" {
  672. worksafe = true
  673. filterHTTPS = true
  674. }
  675. //check if and what is the user posting
  676. switch r.Method {
  677. case "POST":
  678. if err := r.ParseForm(); err != nil {
  679. error.Error = err.Error()
  680. t, _ := template.ParseFiles("coreassets/error.html.go")
  681. t.Execute(w, error)
  682. }
  683. worksafebox := r.Form.Get("worksafe")
  684. agreecheck := r.Form.Get("agree")
  685. agreesubmit := r.Form.Get("agreesubmit")
  686. httpsbox := r.Form.Get("filterHTTPS")
  687. //if user agrees to terms to disable adult content, set cookie and return to index
  688. if agreecheck == "on" {
  689. worksafe = false
  690. //expiration := time.Now().Add(365 * 24 * time.Hour)
  691. if filterHTTPS == false {
  692. cookie := http.Cookie{Name: "ws", Value: "0", Path: "/"}
  693. http.SetCookie(w, &cookie)
  694. } else {
  695. cookie := http.Cookie{Name: "ws", Value: "2", Path: "/"}
  696. http.SetCookie(w, &cookie)
  697. }
  698. p := indexPage{}
  699. t, _ := template.ParseFiles("coreassets/settings/gohome.html")
  700. t.Execute(w, p)
  701. //else if worksafebox is checked, return to index with worksafe on
  702. } else if worksafebox == "on" || agreesubmit == "on" {
  703. //expiration := time.Now().Add(365 * 24 * time.Hour)
  704. if httpsbox != "on" {
  705. cookie := http.Cookie{Name: "ws", Value: "1", Path: "/"}
  706. http.SetCookie(w, &cookie)
  707. } else {
  708. cookie := http.Cookie{Name: "ws", Value: "3", Path: "/"}
  709. http.SetCookie(w, &cookie)
  710. }
  711. p := indexPage{}
  712. t, _ := template.ParseFiles("coreassets/settings/gohome.html")
  713. t.Execute(w, p)
  714. //else if worksafebox unchecked and no cookie, go to content agreement section
  715. } else if worksafebox != "on" && worksafewasoff == false && agreesubmit != "on" {
  716. p := indexPage{}
  717. if httpsbox == "on" {
  718. cookie := http.Cookie{Name: "ws", Value: "3", Path: "/"}
  719. http.SetCookie(w, &cookie)
  720. } else {
  721. cookie := http.Cookie{Name: "ws", Value: "1", Path: "/"}
  722. http.SetCookie(w, &cookie)
  723. }
  724. t, _ := template.ParseFiles("coreassets/settings/agree.html.go")
  725. t.Execute(w, p)
  726. //else if worksafebox unchecked and cookie alredy agreed, go back to index
  727. } else if worksafebox != "on" && worksafewasoff == true {
  728. if httpsbox == "on" {
  729. cookie := http.Cookie{Name: "ws", Value: "2", Path: "/"}
  730. http.SetCookie(w, &cookie)
  731. } else {
  732. cookie := http.Cookie{Name: "ws", Value: "0", Path: "/"}
  733. http.SetCookie(w, &cookie)
  734. }
  735. p := indexPage{}
  736. t, _ := template.ParseFiles("coreassets/settings/gohome.html")
  737. t.Execute(w, p)
  738. }
  739. default:
  740. //load the settings page if no post value
  741. settingspage := settingsPage{}
  742. settingspage.Worksafe = worksafe
  743. settingspage.FilterHTTPS = filterHTTPS
  744. t, _ := template.ParseFiles("coreassets/settings/settings.html.go")
  745. t.Execute(w, settingspage)
  746. }
  747. }
  748. func surprise(w http.ResponseWriter, r *http.Request) {
  749. surprise := surpriseURL{}
  750. //check if worksafe+HTTPS cookie enabled.
  751. filterHTTPS := false
  752. worksafeHTTPSCookie, err := r.Cookie("ws")
  753. if err != nil {
  754. filterHTTPS = false
  755. } else if worksafeHTTPSCookie.Value == "2" {
  756. filterHTTPS = true
  757. } else if worksafeHTTPSCookie.Value == "3" {
  758. filterHTTPS = true
  759. }
  760. //setup for error report
  761. error := errorReport{}
  762. //init the db and set charset
  763. db, err := sql.Open("mysql", "guest:qwer@/wiby?charset=utf8mb4")
  764. if err != nil {
  765. error.Error = err.Error()
  766. t, _ := template.ParseFiles("coreassets/error.html.go")
  767. t.Execute(w, error)
  768. }
  769. defer db.Close()
  770. // Open doesn't open a connection. Validate DSN data:
  771. err = db.Ping()
  772. if err != nil {
  773. error.Error = err.Error()
  774. t, _ := template.ParseFiles("coreassets/error.html.go")
  775. t.Execute(w, error)
  776. }
  777. //grab a random page
  778. var sqlQuery string
  779. if filterHTTPS == false {
  780. sqlQuery = "select url from windex where worksafe = 1 and surprise = 1 order by rand() limit 1"
  781. } else {
  782. sqlQuery = "select url from windex where worksafe = 1 and surprise = 1 and http = 1 order by rand() limit 1"
  783. }
  784. rows, err := db.Query(sqlQuery)
  785. if err != nil {
  786. error.Error = err.Error()
  787. t, _ := template.ParseFiles("coreassets/error.html.go")
  788. t.Execute(w, error)
  789. }
  790. var url string
  791. for rows.Next() {
  792. err := rows.Scan(&url)
  793. if err != nil {
  794. error.Error = err.Error()
  795. t, _ := template.ParseFiles("coreassets/error.html.go")
  796. t.Execute(w, error)
  797. }
  798. surprise.Url = url
  799. }
  800. defer rows.Close()
  801. rows.Close()
  802. db.Close()
  803. t, _ := template.ParseFiles("coreassets/surprise.html.go")
  804. t.Execute(w, surprise)
  805. }
  806. func MysqlRealEscapeString(value string) string {
  807. replace := map[string]string{"\\": "\\\\", "'": `\'`, "\\0": "\\\\0", "\n": "\\n", "\r": "\\r", `"`: `\"`, "\x1a": "\\Z"}
  808. for b, a := range replace {
  809. value = strings.Replace(value, b, a, -1)
  810. }
  811. return value
  812. }
  813. func JSONRealEscapeString(value string) string {
  814. replace := map[string]string{"\\": "\\\\", "\t": "\\t", "\b": "\\b", "\n": "\\n", "\r": "\\r", "\f": "\\f" /*, `"`:`\"`*/}
  815. for b, a := range replace {
  816. value = strings.Replace(value, b, a, -1)
  817. }
  818. //remove control characters
  819. buf := []rune(value)
  820. for i, v := range buf {
  821. if v < 32 || v == 127 {
  822. buf[i]=32
  823. }
  824. }
  825. return string(buf)
  826. }
  827. func substr(s string, start int, end int) string {
  828. start_str_idx := 0
  829. i := 0
  830. for j := range s {
  831. if i == start {
  832. start_str_idx = j
  833. }
  834. if i == end {
  835. return s[start_str_idx:j]
  836. }
  837. i++
  838. }
  839. return s[start_str_idx:]
  840. }
  841. func searchredirect(w http.ResponseWriter, r *http.Request, query string) {
  842. //separate actual query from search redirect
  843. actualquery := ""
  844. redirect := ""
  845. lenquery := len(query)
  846. if strings.Index(query," ") > -1{
  847. location := strings.Index(query, " !")
  848. if location == -1 {
  849. location = strings.Index(query, " &")
  850. }
  851. if location > -1 && strings.Index(query[location+1:lenquery], " ") == -1 { //redirect is at end of query
  852. redirect = query[location+2 : lenquery]
  853. actualquery = query[:location]
  854. } else if (strings.Index(query, "!") == 0 || strings.Index(query, "&") == 0){ //redirect is at start of query
  855. redirect = query[1:strings.Index(query, " ")]
  856. actualquery = query[strings.Index(query, " ")+1:]
  857. //fmt.Printf("\nRedirect: %s\nquery: %s\n",redirect,actualquery)
  858. }
  859. redirect = strings.ToLower(redirect)
  860. }else if (query[0] == '!' || query[0] == '&') && lenquery > 1{
  861. redirect = query[1:]
  862. }
  863. if redirect != "" {
  864. //determine which search engine to redirect
  865. if redirect == "g" { //if google text search
  866. http.Redirect(w, r, "http://google.com/search?q="+actualquery, http.StatusSeeOther)
  867. } else if redirect == "b" { //if bing text search
  868. http.Redirect(w, r, "http://bing.com/search?q="+actualquery, http.StatusSeeOther)
  869. } else if redirect == "gi" { //if google image search
  870. http.Redirect(w, r, "http://www.google.com/search?tbm=isch&q="+actualquery, http.StatusSeeOther)
  871. } else if redirect == "bi" { //if bing image search
  872. http.Redirect(w, r, "http://www.bing.com/images/search?q="+actualquery, http.StatusSeeOther)
  873. } else if redirect == "gv" { //if google video search
  874. http.Redirect(w, r, "http://www.google.com/search?tbm=vid&q="+actualquery, http.StatusSeeOther)
  875. } else if redirect == "bv" { //if bing video search
  876. http.Redirect(w, r, "http://www.bing.com/videos/search?q="+actualquery, http.StatusSeeOther)
  877. } else if redirect == "gm" { //if google maps search
  878. http.Redirect(w, r, "http://www.google.com/maps/search/"+actualquery, http.StatusSeeOther)
  879. } else if redirect == "bm" { //if bing maps search
  880. http.Redirect(w, r, "http://www.bing.com/maps?q="+actualquery, http.StatusSeeOther)
  881. }/* else {
  882. http.Redirect(w, r, "/?q="+actualquery, http.StatusSeeOther)
  883. }*/
  884. }
  885. }