2014-03-17 22:03:58 +04:00
// Copyright 2014 The Gogs Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
2014-02-19 13:50:53 +04:00
package main
import (
2014-03-23 12:31:13 +04:00
"bytes"
"container/list"
2014-02-19 13:50:53 +04:00
"fmt"
2014-03-23 12:31:13 +04:00
"io"
2014-02-19 13:50:53 +04:00
"os"
"os/exec"
"strconv"
"strings"
2014-03-25 13:28:02 +04:00
"time"
2014-02-19 13:50:53 +04:00
"github.com/codegangsta/cli"
2014-03-23 12:51:43 +04:00
"github.com/gogits/gogs/modules/log"
2014-03-21 00:04:56 +04:00
2014-03-23 12:31:13 +04:00
"github.com/gogits/git"
2014-02-19 13:50:53 +04:00
"github.com/gogits/gogs/models"
2014-03-21 00:04:56 +04:00
"github.com/gogits/gogs/modules/base"
2014-02-19 13:50:53 +04:00
)
var (
COMMANDS_READONLY = map [ string ] int {
2014-02-25 10:01:52 +04:00
"git-upload-pack" : models . AU_WRITABLE ,
"git upload-pack" : models . AU_WRITABLE ,
"git-upload-archive" : models . AU_WRITABLE ,
2014-02-19 13:50:53 +04:00
}
COMMANDS_WRITE = map [ string ] int {
"git-receive-pack" : models . AU_READABLE ,
"git receive-pack" : models . AU_READABLE ,
}
)
var CmdServ = cli . Command {
Name : "serv" ,
2014-02-25 10:01:52 +04:00
Usage : "This command just should be called by ssh shell" ,
2014-02-19 13:50:53 +04:00
Description : `
2014-02-25 10:01:52 +04:00
gogs serv provide access auth for repositories ` ,
2014-02-19 13:50:53 +04:00
Action : runServ ,
2014-03-12 08:19:45 +04:00
Flags : [ ] cli . Flag { } ,
2014-02-19 13:50:53 +04:00
}
2014-03-24 18:26:05 +04:00
func init ( ) {
2014-03-24 19:58:46 +04:00
os . MkdirAll ( "log" , os . ModePerm )
2014-03-24 18:26:05 +04:00
log . NewLogger ( 10000 , "file" , fmt . Sprintf ( ` { "filename":"%s"} ` , "log/serv.log" ) )
}
2014-03-23 12:31:13 +04:00
func parseCmd ( cmd string ) ( string , string ) {
ss := strings . SplitN ( cmd , " " , 2 )
if len ( ss ) != 2 {
return "" , ""
}
verb , args := ss [ 0 ] , ss [ 1 ]
if verb == "git" {
ss = strings . SplitN ( args , " " , 2 )
args = ss [ 1 ]
verb = fmt . Sprintf ( "%s %s" , verb , ss [ 0 ] )
}
return verb , args
}
2014-02-19 13:50:53 +04:00
func In ( b string , sl map [ string ] int ) bool {
_ , e := sl [ b ]
return e
}
2014-03-23 12:31:13 +04:00
func runServ ( k * cli . Context ) {
2014-03-21 11:36:26 +04:00
base . NewConfigContext ( )
models . LoadModelsConfig ( )
models . NewEngine ( )
2014-02-19 13:50:53 +04:00
keys := strings . Split ( os . Args [ 2 ] , "-" )
if len ( keys ) != 2 {
fmt . Println ( "auth file format error" )
return
}
keyId , err := strconv . ParseInt ( keys [ 1 ] , 10 , 64 )
if err != nil {
fmt . Println ( "auth file format error" )
return
}
user , err := models . GetUserByKeyId ( keyId )
if err != nil {
fmt . Println ( "You have no right to access" )
return
}
cmd := os . Getenv ( "SSH_ORIGINAL_COMMAND" )
if cmd == "" {
2014-03-16 10:28:24 +04:00
println ( "Hi" , user . Name , "! You've successfully authenticated, but Gogs does not provide shell access." )
2014-02-19 13:50:53 +04:00
return
}
verb , args := parseCmd ( cmd )
2014-02-25 10:01:52 +04:00
rRepo := strings . Trim ( args , "'" )
rr := strings . SplitN ( rRepo , "/" , 2 )
2014-02-19 13:50:53 +04:00
if len ( rr ) != 2 {
2014-02-20 10:53:56 +04:00
println ( "Unavilable repository" , args )
2014-02-19 13:50:53 +04:00
return
}
repoName := rr [ 1 ]
if strings . HasSuffix ( repoName , ".git" ) {
repoName = repoName [ : len ( repoName ) - 4 ]
}
2014-03-16 08:18:34 +04:00
2014-03-25 13:11:13 +04:00
isWrite := In ( verb , COMMANDS_WRITE )
isRead := In ( verb , COMMANDS_READONLY )
2014-03-22 12:44:57 +04:00
repo , err := models . GetRepositoryByName ( user . Id , repoName )
2014-03-23 12:31:13 +04:00
var isExist bool = true
2014-03-16 08:18:34 +04:00
if err != nil {
2014-03-23 12:31:13 +04:00
if err == models . ErrRepoNotExist {
isExist = false
2014-03-25 13:11:13 +04:00
if isRead {
println ( "Repository" , user . Name + "/" + repoName , "is not exist" )
return
}
2014-03-23 12:31:13 +04:00
} else {
2014-03-25 13:11:13 +04:00
println ( "Get repository error:" , err )
2014-03-23 12:31:13 +04:00
return
}
2014-03-16 08:18:34 +04:00
}
2014-03-25 13:11:13 +04:00
// access check
2014-02-19 13:50:53 +04:00
switch {
case isWrite :
2014-02-25 11:28:04 +04:00
has , err := models . HasAccess ( user . Name , repoName , models . AU_WRITABLE )
2014-02-19 13:50:53 +04:00
if err != nil {
2014-02-25 10:01:52 +04:00
println ( "Inernel error:" , err )
2014-02-19 13:50:53 +04:00
return
}
if ! has {
2014-02-25 11:28:04 +04:00
println ( "You have no right to write this repository" )
2014-02-19 13:50:53 +04:00
return
}
case isRead :
2014-02-25 11:28:04 +04:00
has , err := models . HasAccess ( user . Name , repoName , models . AU_READABLE )
2014-02-19 13:50:53 +04:00
if err != nil {
2014-02-25 10:01:52 +04:00
println ( "Inernel error" )
2014-02-19 13:50:53 +04:00
return
}
if ! has {
2014-02-25 11:28:04 +04:00
has , err = models . HasAccess ( user . Name , repoName , models . AU_WRITABLE )
2014-02-19 13:50:53 +04:00
if err != nil {
2014-02-25 10:01:52 +04:00
println ( "Inernel error" )
2014-02-19 13:50:53 +04:00
return
}
}
if ! has {
2014-02-20 10:53:56 +04:00
println ( "You have no right to access this repository" )
2014-02-19 13:50:53 +04:00
return
}
default :
2014-02-20 10:53:56 +04:00
println ( "Unknown command" )
2014-02-19 13:50:53 +04:00
return
}
2014-03-25 13:11:13 +04:00
var rep * git . Repository
repoPath := models . RepoPath ( user . Name , repoName )
2014-02-19 13:50:53 +04:00
if ! isExist {
2014-03-25 13:11:13 +04:00
if isWrite {
_ , err = models . CreateRepository ( user , repoName , "" , "" , "" , false , true )
2014-02-19 13:50:53 +04:00
if err != nil {
2014-02-20 10:53:56 +04:00
println ( "Create repository failed" )
2014-02-19 13:50:53 +04:00
return
}
}
}
2014-03-25 13:11:13 +04:00
rep , err = git . OpenRepository ( repoPath )
if err != nil {
println ( err . Error ( ) )
return
}
2014-03-23 12:31:13 +04:00
refs , err := rep . AllReferencesMap ( )
if err != nil {
println ( err . Error ( ) )
return
}
2014-02-25 10:01:52 +04:00
gitcmd := exec . Command ( verb , rRepo )
2014-03-21 00:04:56 +04:00
gitcmd . Dir = base . RepoRootPath
2014-03-23 12:31:13 +04:00
var s string
b := bytes . NewBufferString ( s )
gitcmd . Stdout = io . MultiWriter ( os . Stdout , b )
2014-03-23 13:10:09 +04:00
//gitcmd.Stdin = io.MultiReader(os.Stdin, b)
gitcmd . Stdin = os . Stdin
2014-02-19 13:50:53 +04:00
gitcmd . Stderr = os . Stderr
2014-03-18 01:00:35 +04:00
if err = gitcmd . Run ( ) ; err != nil {
2014-02-25 10:01:52 +04:00
println ( "execute command error:" , err . Error ( ) )
2014-03-25 13:11:13 +04:00
return
2014-02-19 13:50:53 +04:00
}
2014-03-25 13:11:13 +04:00
if isRead {
2014-03-23 13:10:09 +04:00
return
}
2014-03-25 13:28:02 +04:00
time . Sleep ( time . Second )
2014-03-25 13:11:13 +04:00
// find push reference name
2014-03-23 12:31:13 +04:00
var t = "ok refs/heads/"
var i int
var refname string
for {
l , err := b . ReadString ( '\n' )
if err != nil {
break
}
i = i + 1
l = l [ : len ( l ) - 1 ]
idx := strings . Index ( l , t )
if idx > 0 {
refname = l [ idx + len ( t ) : ]
}
2014-02-19 13:50:53 +04:00
}
2014-03-25 13:11:13 +04:00
if refname == "" {
println ( "No find any reference name:" , b . String ( ) )
return
}
2014-03-23 12:31:13 +04:00
var ref * git . Reference
var ok bool
var l * list . List
//log.Info("----", refname, "-----")
if ref , ok = refs [ refname ] ; ! ok {
2014-03-25 13:11:13 +04:00
// for new branch
2014-03-23 12:31:13 +04:00
refs , err = rep . AllReferencesMap ( )
if err != nil {
println ( err . Error ( ) )
return
}
if ref , ok = refs [ refname ] ; ! ok {
2014-03-24 18:26:05 +04:00
log . Trace ( "unknow reference name -" , refname , "-" , b . String ( ) )
2014-03-23 12:31:13 +04:00
return
}
l , err = ref . AllCommits ( )
if err != nil {
println ( err . Error ( ) )
return
}
} else {
//log.Info("----", ref, "-----")
var last * git . Commit
//log.Info("00000", ref.Oid.String())
last , err = ref . LastCommit ( )
if err != nil {
println ( err . Error ( ) )
return
}
ref2 , err := rep . LookupReference ( ref . Name )
if err != nil {
println ( err . Error ( ) )
return
}
//log.Info("11111", ref2.Oid.String())
before , err := ref2 . LastCommit ( )
if err != nil {
println ( err . Error ( ) )
return
}
//log.Info("----", before.Id(), "-----", last.Id())
l = ref . CommitsBetween ( before , last )
}
commits := make ( [ ] [ ] string , 0 )
2014-03-23 14:00:09 +04:00
var maxCommits = 3
2014-03-23 15:24:06 +04:00
for e := l . Front ( ) ; e != nil ; e = e . Next ( ) {
2014-03-23 12:31:13 +04:00
commit := e . Value . ( * git . Commit )
commits = append ( commits , [ ] string { commit . Id ( ) . String ( ) , commit . Message ( ) } )
2014-03-23 13:51:12 +04:00
if len ( commits ) >= maxCommits {
break
}
2014-03-23 12:31:13 +04:00
}
if err = models . CommitRepoAction ( user . Id , user . Name ,
2014-03-23 14:27:01 +04:00
repo . Id , repoName , refname , & base . PushCommits { l . Len ( ) , commits } ) ; err != nil {
2014-03-23 12:31:13 +04:00
log . Error ( "runUpdate.models.CommitRepoAction: %v" , err , commits )
} else {
2014-03-23 15:53:50 +04:00
c := exec . Command ( "git" , "update-server-info" )
2014-03-25 13:11:13 +04:00
c . Dir = repoPath
2014-03-23 15:53:50 +04:00
err := c . Run ( )
if err != nil {
log . Error ( "update-server-info: %v" , err )
}
2014-02-19 13:50:53 +04:00
}
}