우선 FTP 에서 심볼릭링크(symbolic link)는 비표준이라고 합니다.


빠르게 최근 데이터들을 정리해서 보되, 사본은 만들지 않고 심볼릭링크로 접근하고 있던 부분을 쓰려고 보니 chroot 때문에 접근이 불가능...


보통 "mount --bind" 를 symbolic link 대신 사용하는 방법을 해결안으로 제시하는데 이건 root 권한이 매번 필요.



/volumes/ftpsrv 를 FTP에서 chroot로 쓰고 있었고 이 안에 data 디렉토리가 각종 데이터를 담고 있다.

( /volumes/ftpsrv/data )


FTP 클라이언트마다 되는 놈도 있고 안되는 놈도 있지만, 주로 쓰는 FileZilla 는 chroot 상에서 해당 경로로 접근해주긴 한다.


ln -s /volumes/ftpsrv/data/dir1/target /volumes/ftpsrv/data/dir2/s_target

이렇게 만들어봐야 chroot 로 보면 없는 경로 이므로 접근하지 못한다.


그럼  ln -s /data/dir1/target /volumes/ftpsrv/data/dir2/s_target 는?

안된다. 째째하게도 심볼릭링크 대상 파일이 없으면 만들지 못한다.


이걸 어쩔까 하다가


bind mount를 많이 쓰는 방법이 생각나

/data 디렉토리를 만들었고 

mount --bind /volumes/ftpsrv/data /data


하니,

실제 파일이 존재하므로

ln -s /data/dir1/target /volumes/ftpsrv/data/dir2/s_target

이런 형태로 심볼릭링크도 생성할 수 있고, 


FileZilla 는 chroot 된 경로내에 같은 경로상 파일이 존재해서 접근이 가능하다. -_-;;;

...걍 루트로 mount 돌릴까...



Posted by freezn

https://pve.proxmox.com/wiki/Manual:_pct


 pct exec <vmid> [<extra-args>]

    Launch a command inside the specified container.

    <vmid>     integer (1 - N)

              The (unique) ID of the VM.

    <extra-args> array

              Extra arguments as array

 


pct exec <vmid> passwd <username>





Posted by freezn

XE, XpressEngine 1.8.20 기준.



사진 파일 첨부를 위해 ckeditor를 적용했고,

[xe, xpressengine - 모바일 ckeditor적용, 사진 파일 첨부]



ckeditor를 적용하니 ckeditor가 자연스럽게 적용되지 않아 모바일 여부 확인하여 툴바를 접고 높이를 줄이도록 하였다.

[xe, xpressengine - 모바일에서 ckeditor 크기줄이기, 툴바접기]


하지만 또 발생하는 문제는

ckeditor로 글을 작성할 경우 기본적으로 html 모드이고

모든 엔터가 nl2br 처리 되어 두번엔터된 효과가 나타났다.


해결은 글 입력/수정 시 모바일여부를 확인하고 nl2br을 붙여주는 부분을 수정한다.


파일 : xe/modules/document/document.controller.php


2곳을 고쳐야한다.

위 파일을 열어 Mobile로 검색해보면, 284라인(insertDocument)과 486라인(updateDocument) 근처에 다음과 같이 되어있다.


if(Mobile::isFromMobilePhone())

{

    if($obj->use_html != 'Y')

    {

        $obj->content = htmlspecialchars($obj->content, ENT_COMPAT | ENT_HTML401, 'UTF-8', false);

    }

    $obj->content = nl2br($obj->content);

}


아래처럼 if문 안으로 수정한다.


if(Mobile::isFromMobilePhone())

{

    if($obj->use_html != 'Y')

    {

        $obj->content = htmlspecialchars($obj->content, ENT_COMPAT | ENT_HTML401, 'UTF-8', false);

        $obj->content = nl2br($obj->content);

    }

}


모바일에서 html 모드일 경우 "<br>" 태그를 입력하기 너무 번거롭기 때문에 이렇게 작성되어있지만 아예 위즈윅에디터를 적용했기 때문에 자동으로 줄넘김 태그로 변환해줄 필요가 없기 때문.


Posted by freezn

XE, XpressEngine 1.8.20 기준.


아직 스킨을 수정해야한다.


수정하려는 스킨의 write_form.html 에서 본문 입력란(일반적으로 textarea)을

{$oDocument->getEditor()} 

로 대체한다.


처음엔 ckeditor 없이 파일만 첨부하도록 하고 싶었지만 파일첨부만을 해주는 기능은 찾지 못했고

getEditor 함수가 xe 설정상 기본 에디터와 파일첨부창을 함께 호출하므로 간편하게 적용이 가능하다.


대신 자연스러운 적용을 위해

[xe, xpressengine - 모바일에서 ckeditor 크기줄이기, 툴바접기]


를 함께 적용한다.




Posted by freezn

XE, XpressEngine 1.8.20 기준


XE 사용 중 모바일모드에도 사진첨부를 위해 스킨을 수정해서 ckeditor를 적용했다.

그런데 기본상태에서 툴바는 너무 크고 에디터 입력창 자체가 높아 불편하다.


그래서 툴바는 접어놓고, 크기를 줄여보기로 한다.


1차 시도 (실패) :

스킨에서 추가하는 js 에서 ckeditor 에 접근...

ckeditor 호출이 느려, 호출이 끝난 후 다시 접고 줄여야 하는데 문제가 발생한다.


2차 시도 (성공) : 

ckeditor는 설정파일이 있다.

모바일인지를 간단히 체크해서, 툴바를 펼치지 않고 높이도 지정해서 로딩되도록 한다.

xe에서 ckeditor의 설정파일은 비어있거나 없을 수 있다. 없으면 만들어준다.

파일 : xe/common/js/plugins/ckeditor/ckeditor/config.js



function checkMobileDevice() {

        var mobileKeyWords = new Array('Android', 'iPhone', 'iPod', 'BlackBerry', 'Windows CE', 'SAMSUNG', 'LG', 'MOT', 'SonyEricsson');

        for (var info in mobileKeyWords) {

                if (navigator.userAgent.match(mobileKeyWords[info]) != null) {

                        return true;

                }

        }

        return false;

}


if(checkMobileDevice()==true)

{

        CKEDITOR.editorConfig = function( config ) {

                config.toolbarStartupExpanded = false; // 툴바 접기

                config.height = 230; // 높이 설정

                config.removePlugins = 'liststyle,tabletools,contextmenu'; // context menu disable

        };

        CKEDITOR.replace( 'iframe', {

                 removePlugins: 'contextmenu,tabletools' // context menu disable

        } );

}


붙여넣기를 위해 롱터치 시 번쩍! 나타나는 ckeditor의 context menu 때문에 붙여넣기가 불가능했는데 context menu disable 관련 설정을 찾아 해결했다. 사실 아주 매끄럽지는 못하다.


+

checkMobileDevice() 는 너무 고전적이라 안되는 경우가 있을 수 있다.

https://github.com/kaimallea/isMobile/blob/master/isMobile.js

사용해도 되고...

Posted by freezn


cloudflare를 접해봤다.

워낙 잘되어있고 성능향상이나 서버ip감추기, 서버 성능 영향없는 ssl 적용 등 좋지만


이를 위해 http proxy(cdn)을 활성화 시키고 나면, 클라우드플레어 서버를 거쳐 오기 때문에

서버에 남는 사용자의 접속ip가 실제사용자 ip가 아닌 클라우드플레어 ip로 제공되는 바람에

기존 프로그램들이 말썽을 일으키는 경우가 많다.


특히 flexible/full/strict ssl은 클라우드플레어를 신뢰하지 않는다면 사용할 수 없는 기능.

여러 도메인을 모두 인증해주는 ECC 도메인 인증서기 때문에 인증서의 발급대상이 XXX.cloudflaressl.com 이다.

flexible/full/strict 관계 없이 모두 같다.


full/strict 을 사용하여도 서버와 클라우드플레어간의 전송구간, 사용자와 클라우드플레어와의 전송구간 이 암호화 되는 것이지 클라우드플레어가 컨텐츠를 볼 수 없는 것이 아니다.



막그린 그림이고 flexible ssl 적용시에 대한 예시이지만 대충 뭐 저렇다.


ssl을 적용하지 않아도 http proxy(cdn)기능을 활성화 하면 모든 통신은 클라우드플레어가 캐싱한다.

이때 사진이나 js, css 같은 정적인 자료는 캐싱되기 때문에 서버의 성능향상이 이루어진다.

하지만 프록시를 사용할 경우 개념상 사용자가 서버에 접속한 게 아니라 위 그림처럼 클라우드플레어가 사용자 대신 받았다가 사용자에게 보내주는 것이기 때문에 서버관점에서 접속한 사용자는 클라우드플레어 이다.


PHP의 경우 $_SERVER['REMOTE_ADDR'] 에 클라우드플레어의 IP가 나타난다. JSP, ASP, node.js 가릴 것 없이 당연히 모두 마찬가지다.


클라우드플레어는 이러한 점을 보완하기 위해 CF_로 시작하는 확장변수들을 제공한다.

참고 : http://stackoverflow.com/questions/14985518/cloudflare-and-logging-visitor-ip-addresses-via-in-php

PHP의 경우 $_SERVER["HTTP_CF_CONNECTING_IP"] 를 사용하면 실제 IP를 얻을 수 있다.



하지만 xe 같은 범용 프로그램을 사용하는 경우 이것의 관리가 너무 불편하다.

nginx의 경우 이런 프록시서버를 통한 접속의 경우를 위해 real_ip 모듈이 제공된다.

ubuntu 14.04 lts를 기준으로 다음 설정을 /etc/nginx/conf.d/cloudflare_realip.conf 파일로 만들어 넣고


set_real_ip_from 103.21.244.0/22;

set_real_ip_from 103.22.200.0/22;

set_real_ip_from 103.31.4.0/22;

set_real_ip_from 104.16.0.0/12;

set_real_ip_from 108.162.192.0/18;

set_real_ip_from 131.0.72.0/22;

set_real_ip_from 141.101.64.0/18;

set_real_ip_from 162.158.0.0/15;

set_real_ip_from 172.64.0.0/13;

set_real_ip_from 173.245.48.0/20;

set_real_ip_from 188.114.96.0/20;

set_real_ip_from 190.93.240.0/20;

set_real_ip_from 197.234.240.0/22;

set_real_ip_from 198.41.128.0/17;

set_real_ip_from 199.27.128.0/21;

set_real_ip_from 2400:cb00::/32;

set_real_ip_from 2606:4700::/32;

set_real_ip_from 2803:f800::/32;

set_real_ip_from 2405:b500::/32;

set_real_ip_from 2405:8100::/32;


# use any of the following two

real_ip_header CF-Connecting-IP;

#real_ip_header X-Forwarded-For;


sudo nginx -s reload 명령 한번 내려 설정 리로딩시켜주면 이후에는 원 아이피가 REMOTE_ADDR 같은 환경 변수에 담겨오게 된다.

xe의 조회수, 카운터 같은 기능들은 전적으로 ip에 의존하기 때문에 프로그램을 고치거나 위 설정을 적용해서 해결이 가능하다.


혹시 서버가 haproxy같은 또 다른 프록시를 사용하고 있다면 해당 ip도 위 설정에 적어서 해결할 수 있다.

이때에는 real_ip_header가 CF-Connecting-IP가 아닌 X-Forwarded-For 같은 범용 헤더이거나 또 다른 전용헤더일 수 있으니 해당 헤더를 확인.


Posted by freezn

현상 : ckeditor가 간헐적 혹은 아예 나오지 않음


해결 : Speed -> Rocket Loader™ 를 OFF



Posted by freezn

1.8.20 기준


참고 : https://www.xpressengine.com/tip/20953744


파일 : /classes/mail/Mail.class.php 133라인


Mail 생성자에

$this->useGmailAccount("계정", "패스워드");

추가


- 번거롭지만 xe 업데이트 시 다시 수정 필요

- gmail 2단계인증을 사용 중일 경우 계정->보안 에서 앱패스워드를 생성해서 사용

- 발송용 gmail 계정을 따로 만들어서 사용하는 것을 추천



Posted by freezn

PHP cli (5.3.3)로 스크립트를 하나 만들어놨다.

파일 처리한 후 rename 함수로 백업 경로로 옮기는 스크립트인데

용량 확보와 속도 개선을 위해 추가 디스크를 마운트 시켜놓고 나니 이런 오류가 발생했다.

[PHP] PHP Warning:  rename(/AAA/XXX,/BBB/XXX):
Operation not permitted in XXX.php on line XX

황당하게도 처리는 제대로 되었고 경고만 뜬다.


http://kr1.php.net/manual/en/function.rename.php


원인을 찾아보니 php 의 rename은 리눅스의 rename 명령을 이용하는 것이다보니 본래는 디스크간 이동을 위한 것이 아니다.

그러므로 디스크간 이동을 위해서는 system 이나 exec 함수로 식으로 수정하거나


exec("mv /AAA/XXX /BBB/XXX") ;

or

if(copy('/AAA/XXX', '/BBB/XXX')==TRUE) unlink('/AAA/XXX');

식으로 수정하면 경고 없이 깔끔하게 된다.

Posted by freezn

zend_parse_parameters 함수를 사용합니다.

아마 매크로겠지만 그런 것 까지 신경쓰진 않습니다. ㅎㅎ


함수 만들기 기본 설명, 상세 파라미터 종류 들은 http://www.php.net/manual/en/internals2.funcs.php 에서 볼 수 있습니다..만 퍼옵니다.

 Spec

 Type

 Locals

 a

 array

 zval*

 A

 array or object

 zval*

 b

 boolean

 zend_bool

 C

 class

 zend_class_entry*

 d

 double

 double

 f

 function

 zend_fcall_info*, zend_fcall_info_cache*

 h

 array

 HashTable*

 H

 array or object

 HashTable*

 l

 long

 long

 L

 long (limits out-of-range LONG_MAX/LONG_MIN)

 long

 o

 object

 zval*

 O

 object (of specified zend_class_entry)

 zval*, zend_class_entry*

 p

 string (a valid path)

 char*, int

 r

 resource

 char*

 s

 string

 char*, int

 z

 mixed

 zval*

 Z

 mixed

 zval**


3번째 파라메터 문자열은 어떤 변수타입들을 받을지, 4번째 이후는 받을 변수들입니다.

zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "받을변수타입들", &변수1, &변수2, ....)


간단히 예시로 정리하면..

// bool : zend_bool
PHP_FUNCTION(test_func1)
{
	zend_bool bool_test;
	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "b", &bool_test) == FAILURE) {
		RETURN_NULL();
	}
	
	if(bool_test==1)
	{
		// true
	}
	else
	{
		// false
	}

	RETURN_NULL();
}

// long
PHP_FUNCTION(test_func2)
{
	long long_test;
	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &long_test) == FAILURE) {
		RETURN_NULL();
	}
	RETURN_NULL();
}

// string
PHP_FUNCTION(test_func3)
{
	char* str_test;
	int str_test_len;
	// 문자열의 경우 파라메터가 무조건 2개임을 명심..
	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &str_test, &str_test_len) == FAILURE) {
		RETURN_NULL();
	}
	RETURN_NULL();
}



여러개일 경우는 그냥 타입을 여러개 쓰고 문자열같은 경우는 깊이값도 같이 받아주면 됩니다.


// multiple params
PHP_FUNCTION(test_func4)
{
	long a;
	double b;
	zend_bool c;
	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ldb", &a, &b, &c) == FAILURE) {
		RETURN_NULL();
	}
	RETURN_NULL();
}


// multiple params with string
PHP_FUNCTION(test_func5)
{
	long a;
	double b;
	zend_bool c;
	char * d;
	int d_len;
	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ldb", &a, &b, &c, &d, &d_len) == FAILURE) {
		RETURN_NULL();
	}
	RETURN_NULL();
}



리턴도 간단합니다.

자세한건 이 링크를 참고하면 되고.. http://docstore.mik.ua/orelly/webprog/php/ch14_08.htm


다른 타입들도 맞춰서 하면 됩니다. 잘 정리되어있네요.

RETURN_RESOURCE(int r);
RETURN_BOOL(int b) ;
RETURN_NULL( );
RETURN_LONG(int l) ;
RETURN_DOUBLE(double d) ;
RETURN_STRING(char *s, int dup) ;
RETURN_STRINGL(char *s, int l, int dup);
RETURN_EMPTY_STRING( );
RETURN_FALSE ;
RETURN_TRUE;
RETVAL_RESOURCE(int r);
RETVAL_BOOL(int b);
RETVAL_NULL( );
RETVAL_LONG(int l);
RETVAL_DOUBLE(double d);
RETVAL_STRING(char *s, int dup);
RETVAL_STRINGL(char *s, int l, int dup);
RETVAL_EMPTY_STRING( );
RETVAL_FALSE;
RETVAL_TRUE;

// return null
PHP_FUNCTION(test_func_ret1)
{
	RETURN_NULL();
}

조금 골치 아픈 건 배열이나 클래스인데 배열만 좀 써보면..

// assoc_array
PHP_FUNCTION(test_func_ret2)
{
	// return_value 는 zval* 타입입니다.
	// 선언은? 글로벌인지 로컬인지 모르겠지만 어딘가에서 해놨나봅니다. 다시 선언하면 오류납니다.
	array_init(return_value);

	add_assoc_long(return_value, "x", 123);
	add_assoc_double(return_value, "y", 456.7);
}

이러면 됩니다.

따로 return 명령을 내리지 않아도 리턴이 됩니다.

PHP에서 받아보면 이런 형태죠

<?php
$ret = test_func_ret2();
var_dump($ret);
?>

array(2) {

["x"]=>

int(123)

["y"]=>

float(456.7)

}


그럼 배열 안의 배열은 ? (subarray)

어떤 함수가 있는지는 PHP 페이지에 있습니다. http://www.php.net/manual/ro/internals2.variables.arrays.php


// sub_array PHP_FUNCTION(test_func_ret3) { zval *subarray; // 요걸 하나 만들어둡니다. int i; array_init(return_value); // 당연히 초기화 하고 for(i=0; i<3; i++) // 3개를 추가해보겠습니다. { MAKE_STD_ZVAL(subarray); // 이름봐선 데이터를 널이나 0으로 채워주는 놈인가봅니다... array_init(subarray); // 추가할 서브배열도 초기화하고 // 값들을 집어넣습니다. add_assoc_long(subarray, "x", 123); add_assoc_double(subarray, "y", 456.7); // 리턴할 값에 서브배열을 추가합니다. add_next_index_zval(return_value, subarray); } }


결과를 찍어보면?

<?php
$ret = test_func_ret3();
var_dump($ret);
?>

요렇게 나오겠죠

array(3) {

[0]=>

array(2) {

["x"]=>

int(123)

["y"]=>

float(456.7)

}

[1]=>

array(2) {

["x"]=>

int(123)

["y"]=>

float(456.7)

}

[2]=>

array(2) {

["x"]=>

int(123)

["y"]=>

float(456.7)

}

  }


Posted by freezn